Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
The following code is taken from spm_matrix.m (GPL: Wellcome Dept of Imaging Neuroscience, author K.Friston).
{{{
P(1) - x translation
P(2) - y translation
P(3) - z translation
P(4) - x rotation about - {pitch} (radians)
P(5) - y rotation about - {roll} (radians)
P(6) - z rotation about - {yaw} (radians)
P(7) - x scaling
P(8) - y scaling
P(9) - z scaling
P(10) - x affine
P(11) - y affine
P(12) - z affine
A - affine transformation matrix
This code returns a matrix defining an orthogonal linear (translation, rotation, scaling or affine) transformation given a vector of parameters (P). The transformations are applied in the following order (i.e., the opposite to which they are specified): 1) shear 2) scale 3) rotation - yaw, roll & pitch 4) translation
SPM uses a PRE-multiplication format i.e. Y = A*X where X and Y are 4 x n
matrices of n coordinates.
T = [1 0 0 P(1);
0 1 0 P(2);
0 0 1 P(3);
0 0 0 1];
R1 = [1 0 0 0;
0 cos(P(4)) sin(P(4)) 0;
0 -sin(P(4)) cos(P(4)) 0;
0 0 0 1];
R2 = [cos(P(5)) 0 sin(P(5)) 0;
0 1 0 0;
-sin(P(5)) 0 cos(P(5)) 0;
0 0 0 1];
R3 = [cos(P(6)) sin(P(6)) 0 0;
-sin(P(6)) cos(P(6)) 0 0;
0 0 1 0;
0 0 0 1];
Z = [P(7) 0 0 0;
0 P(8) 0 0;
0 0 P(9) 0;
0 0 0 1];
S = [1 P(10) P(11) 0;
0 1 P(12) 0;
0 0 1 0;
0 0 0 1];
A = T*R1*R2*R3*Z*S;
}}}
!Welcome to your ''tiddlyspot.com'' ~TiddlyWiki!
''[[tiddlyspot.com|http://tiddlyspot.com]]'' gives you an instant [[TiddlyWiki|http://tiddlywiki.com]] hosted on our ''tiddlyspot.com'' servers.
Want to work online? No problem, you can go to your ''tiddlyspot.com'' URL (which is http://brainimaging.tiddlyspot.com/ ) and start editing. Click "save to web" and your changes are saved directly to your ''tiddlyspot.com'' home -- no messing about with local files or ftp.
Want to work offline? No problem, your ''tiddlyspot.com'' ~TiddlyWiki is a real, fully functioning ~TiddlyWiki that you can save onto your hard drive or USB stick. Use the link below to save to your local computer. As you make changes, use the "save to disk" button to save to your local file. Whenever you're ready to sync up again, just click "save to web".
!To save online
Enter the upload password provided when you created your ~TiddlyWiki. Then click the "save to web" button below (or in the right side column) to save your ~TiddlyWiki.
Upload Password: <<option pasUploadPassword>>
<<upload http://brainimaging.tiddlyspot.com/store.cgi index.html . . brainimaging>>
!To save offline
To take this ~TiddlyWiki offline, click [[Download|http://tiddlyspot.com/?action=download&site=brainimaging]]. Save the file locally then open it in your browser.
!Learn more about ~TiddlyWiki
Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki.
The [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]] is an excellent place to ask questions and get help.
!Enjoy!
We hope you like using your ''tiddlyspot.com'' ~TiddlyWiki. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments.
----
/***
!Metadata:
|''Name:''|ArchivedTimeline|
|''Description:''|Timeline archived monthly.|
|''Version:''|0.7.0|
|''Date:''|Aug 25, 2007|
|''Source:''|http://sourceforge.net/project/showfiles.php?group_id=150646|
|''Author:''|BramChen (bram.chen (at) gmail (dot) com)|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.0.11|
|''Browser:''|Firefox 1.5+; InternetExplorer 6.0|
!Syntax:
{{{<<timeline [modified|created [maxentries [dateFormate]]]>>}}}
!Examples:
{{{<<timeline>>}}}
{{{<<timeline created 10>>}}}
{{{<<timeline modified 10 "MMM DD, YYYY">>}}}
!Revision History:
|''Version''|''Date''|''Note''|
|0.7.0|Jul 25, 2006|Accept a date format parameter|
|0.6.3|Jan 14, 2007|Cleaned codes, Removed config.macros.timeline.slider and config.macros.timeline.onClickSlider|
|0.6.2|Dec 10, 2006|Add monthFormat to display month format for Chinese|
|0.6.1|Aug 12, 2006|A great effect on config.macros.timeline.slider for Firefox, thanks Bob McElrath|
|0.6.0|Jul 25, 2006|Runs compatibly with TW 2.1.0 (rev #403+)|
|0.5.2|Jun 21, 2006|Fixed bugs for dateFormat of TW 2.1|
|~|~|Change default dateFormat to "0DD MMM, YYYY"|
|0.5.1|Jun 04, 2006|Added config.macros.archivedTimeline.orderBy for localization|
|0.5.0|Apr 19, 2006|Fixed bug for twice records of the same date ()|
|~|~|Added Date.prototype.convertToLocalYYYYMMDDHHMM<<br>>in order to backward compatible with 2.0.6-|
|0.4.0|Apr 03, 2006|Added new parameter, {{{<<timeline [sortfield] [maxentries]>>}}}|
|~|~|Added config.options.txtTimelineMaxentries|
|0.3.1|Feb 04, 2006|JSLint checked|
|0.3.0|Feb 04, 2006|Fixed several missing variable declarations|
|0.2.0|Dec 26, 2005|changed for the new feature of Macro timeline of TW 2.0.0 beta 6|
|0.1.0|Nov 3, 2005|Initial release|
!Code section:
***/
//{{{
version.extensions.archivedTimeline = {major: 0, minor: 7, revision: 0,
date: new Date("Aug 26, 2007"),
name: "ArchivedTimeline",
type: "Macro",
author: "BramChen",
source: "http://sourceforge.net/project/showfiles.php?group_id=150646"
};
config.options.txtTimelineMaxentries=0;
config.macros.archivedTimeline = {
tooltips: "Archives sorted by ",
orderBy:{modified: "modified", created: "created"},
monthFormat: "0DD MMM YYYY",
dateFormat: "0DD MMM YYYY"
};
config.macros.timeline = config.macros.archivedTimeline;
config.macros.timeline.handler = function(place,macroName,params) {
var field = params[0] ? params[0] : "modified";
place.appendChild(document.createTextNode(this.tooltips + this.orderBy[field]));
var tiddlers = store.reverseLookup("tags","excludeLists",false,field);
var lastMonth = ""; var lastDay = ""; var theText = "----\n"; var i = 0;
var last = (params[1])?params[1]:config.options.txtTimelineMaxentries;
last = (isNaN(last)||last<1) ? 0:tiddlers.length-Math.min(tiddlers.length,parseInt(last));
var dateFormat = params[2] ? params[2] : this.dateFormat;
var cookie; var archives;
for (var t=tiddlers.length-1; t>=last; t--) {
var tiddler = tiddlers[t];
var theMonth = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,6);
var theDay = tiddler[field].convertToLocalYYYYMMDDHHMM().substr(0,8);
if(theMonth != lastMonth) {
if (lastMonth === "") {
lastMonth = theMonth;
}
else {
place.appendChild(document.createElement('hr'));
cookie = 'chktimeline'+(i++);
archives = this.formatString(this.monthFormat, lastMonth);
var panel = config.macros.slider.createSlider(place,cookie,archives,this.tooltips + archives);
wikify(theText,panel);
lastMonth = theMonth; theText = '----\n';
}
}
if(theDay != lastDay){
theText += tiddler[field].formatString(dateFormat) + '\n';
lastDay = theDay;
}
theText += '* [[' + tiddler.title + ']]\n';
}
place.appendChild(document.createElement('hr'));
cookie = 'chktimeline'+(i++);
archives = this.formatString(this.monthFormat, lastMonth);
var panel = config.macros.slider.createSlider(place,cookie,archives,this.tooltips + archives);
wikify(theText,panel);
place.appendChild(document.createElement('hr'));
};
config.macros.timeline.formatString = function(template, yyyymm)
{
var dateString = new Date(yyyymm.substr(0,4)+'/'+yyyymm.substr(4,2)+'/01');
template = template.replace(/DDD|0DD|DD/g,'');
return dateString.formatString(template);
};
if (!Date.prototype.convertToLocalYYYYMMDDHHMM){
Date.prototype.convertToLocalYYYYMMDDHHMM = function(){
return(String.zeroPad(this.getFullYear(),4) + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2));
}
}
//}}}
See [[License|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].
Miscellaneous Bonus Material
1. Create Mosaic images
Display on page different anatomical slices overlaid with activation. Usually done with group analyses.
1. Toolboxes -> CBMG tools -> Mosaic
2. Select anatomical volume on which to overlay activation - usually mean image
3. Superimpose SPM - yes
4. Select the .mat file containing the contrast activation you want to overlay
5. Select the contrast you want to see, proceed as usual
6. Orientation - axial
7. Starting coordinate = -48 or -51
8. Rows = 6
9. Columns = 6
10. Slice thickness = 3
11. Mosaic progression - ascending
12. Contrast enhance background - no
13. Interpolation type - point
14. Background color - black
15. Draw ROI... - no
Then you have to repeat all steps to see top portion of volume because usually all slices won't fit on one page. Use starting coordinate = 57mm or whatever would be next slice
2. Display activation blobs from multiple contrasts on 1 anatomical volume
15. Display
16. Select the T1 or mean image on which you want to overlay activation
17. The 3 orientations of the image will come up on the main graphics window.
18. Click add blobs in the lower right corner of that window.
19. Number of blobs = the number of contrasts you want to overlay.
20. It will ask you to select the SPM.mat file. Choose the SPM.mat file from the analysis that contains the first contrast you want to look at.
21. The contrast manager window will pop up. Choose the contrast you want to view.
22. Go through the masking/thresholding options as you usually would when viewing a contrast.
23. Finally, it will ask you to select a color to represent activation from that contrast. Pick one. If you pick clashing colors, SPM will quit (just kidding).
24. Repeat steps 6-9 for each contrast that you want to view.
25. The activations will then be overlaid on to the anatomical image you selected. You can click around the anatomical image to view any area.
3. MarsBar ROI Analysis (in spm99)
Compute statistics over a defined region of interest (instead of over whole volume).
This is available in spm2 but I'm not sure if it is fully installed on CNADC workstations.
1. Make directory for this ROI and cd into it
2. Toolboxes -> marsbar
3. ROI definition -> Build
4. Type of ROI
16. activation cluster = select the .mat file from which you want to get the cluster
17. sphere with x y z center coordinates and specify radius
5. Description and name of ROI
6. ROI will be saved as ROI.mat files
7. Build all ROIs before moving in to estimating (?? True??)
8. Design -> Estimate ROI(s)
18. select spmcfg.mat from fixed effects or random effects
9. Select appropriate ROI.mat
10. Summary function - 1st eigenvector (takes variance into account)
11. Creates a marsestimated.mat file (corrected just for this area, not whole brain)
12. Results in MarsBar window
a) Can import contrasts already made from appropriate xCon.mat
b) Or can go straight to statistics table to define a new contrast (name it and type 1 in vector area). This will print out t and p statistics in Matlab window.
4. Optimized VBM (Voxel-Based Morphometry)
An optimized VBM button is under the CBMG tools list on the CNADC workstations. Just press the button, select your T1, and have a cup of java. The batch script is from John Ashburner. DRG comments about the script:
I have commented the script so you can see the command for each of the steps. They are largely the same as we discussed, but for spm2 the modulation step is now done within the program called spm_write_sn. In spm99 it was called spm_preserve_quantity.
Also when we were discussing VBM today there was a value called "cutoff" under both segmentation and normalization and I wasn't sure what that meant. It stands for the cutoff in mm of the period of the cosine basis functions. It is selected to be 25 mm as a default. Roughly translated this means the program will only estimate warps on the order of 25 mm or larger. This gives you an idea of how basically coarse the normalization is. Selecting a finer value might work but you might also see distortions. In spm99 this was called the number of non-linear basis functions. I think the current value name is more intuitive. It was harder to know exactly what 7x8x7 basis functions meant.
To compare Right and Left hemispheric atrophy:
Note: Once the symmetric templates have been created once, they can be saved and reused.
1. Create symmetric templates.
You will need to make symmetric templates for normalization and segmentation.
Copy the following files to a directory of your choosing- all directory references are relative to the SPM2 directory which is in /usr/contrib/spm2
templates/T1.mnc
apriori/gray.mnc
apriori/white.mnc
apriori/csf.mnc
apriori/brainmask.mnc
2. Rename all these files by putting an r in front of the name.
2a. Now start SPM2 and cd to the directory where you just put these images.
3. Click on the display button and choose one of these images.
4. When the display comes up there will be a set of parameters you can change on the left side of the display window.
5. Type -1 in the resize {x} parameter and press the enter key. The image will flip.
6. Click the Reorient images button at the bottom of the graphics window. A dialog will come up that will say "This will flip the images" Click OK. Then choose the image you just displayed. So if you had displayed rT1.mnc then choose rT1.mnc again. This will add a mat file for the image. The mat file contains information about how the image is now flipped.
7. Do this with the rest of the above images.
If you get an error in any of the above it probably means you tried to flip the images in the original spm2 directory which you do not have permission to do. Be careful about choosing the images to flip.
Now you have to average the flipped and unflipped images:
8. Click the IMCALC button (2nd row from bottom, 2nd button from right). You will be asked to select images- choose one of the images you just flipped and its unflipped counterpart which is still in the original spm directory. So for example choose rT1.mnc and T1.mnc. You will then be asked for a name for the output image- call it something like avgT1 (you don't need to put in the 3 letter extension- spm will take care of that).
9. You will then be asked for an equation to calculate- enter the following: (i1 + i2)/2 and hit the return key.
10. Do this for all the images above.
11. You will have to re-threshold the avgbrainmask image you''ve made.
12. Click IMCALC again and choose the avgbrainmask image. Give it a name of threshavgbrainmask and the equation is i1>0. All this is telling the program to do is set every voxel with a value greater than 0 to have a value of 1. This gets rid of all the 0.5 values that were in the averaged image. Do not do this for the other images as they are not mask images.
13. Run the optimizedAVGVBM script- go to the directory the script is in and type optimizedAVGVBM. Just as with the original script you will be asked to choose the T1 images to segment. Don't worry about making copies of the original images as I will make sure the segmented output images are written with a different name. Instead of being named Gfilename I will call them avgGfilename and avgWfilename
14. Once the segmentation is done you will need to create a flipped version of the segmented images. Just worry about the gray matter images. Copy them to a new directory and flip the copied images as above. You will then have 2 sets of gray matter images. All images will have been normalized to a symmetrical template. One set of gray matter images will be unflipped (L on L) and the other set will be flipped (L on R).
15. Now you can take each groups image and compare flipped and unflipped using a paired t-test.
16. Did anyone actually read this far? Congratulations! You win ten points and chocolate from the lab!
Add News of updated tiddlers to your Google home page by entering this URL
{{{http://brainimaging.tiddlyspot.com/index.xml}}}
or by clicking this button
<html>
<a href="http://fusion.google.com/add?feedurl=http%3A//brainimaging.tiddlyspot.com/index.xml"><img src="http://buttons.googlesyndication.com/fusion/add.gif" width="104" height="17" border="0" alt="Add to Google"></a>
</html>
SPM computes an image of local smoothness, or Resels per Voxel as part of the random field theory calculations. However, RPV values are difficult to interpret, and it is much easier to understand image smoothness by computing a FWHM (full width at half maximum) image. The relationship is:
{{{
FWHM = RPV.^(-1/3)
}}}
The FWHM is easily obtained by using imcalc.
*The input image is ''RPV.img''.
*The expression is: ''i1.^(-1/3)''
**Make sure you enter a ''.'' before the ''^''.
To calculate the FWHM in a specified ROI from the RPV image
*get the RPV value from the RPV image, for example by using the [[mksphroi]] function.
*''FWHM = (V/RPV)^(1/3)'' where V = number of voxels in ROI.
See the article by Hayasaka et al., for discussion on the effects of non-stationarity of smoothness on parametric and non-parametric stats. [[Pubmed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&dopt=Citation&list_uids=15193596]]
/***
|Name|CalendarPlugin|
|Source|http://www.TiddlyTools.com/#CalendarPlugin|
|Version|2008.02.27|
|Author|Eric Shulman|
|Original Author|SteveRumsby|
|License|unknown|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|display monthly and yearly calendars|
NOTE: For enhanced date display (including popups), you must also install [[DatePlugin]]
!!!!!Usage:
<<<
|{{{<<calendar>>}}}|Produce a full-year calendar for the current year|
|{{{<<calendar year>>}}}|Produce a full-year calendar for the given year|
|{{{<<calendar year month>>}}}|Produce a one-month calendar for the given month and year|
|{{{<<calendar thismonth>>}}}|Produce a one-month calendar for the current month|
|{{{<<calendar lastmonth>>}}}|Produce a one-month calendar for last month|
|{{{<<calendar nextmonth>>}}}|Produce a one-month calendar for next month|
<<<
!!!!!Configuration:
<<<
|''First day of week:''<br>{{{config.options.txtCalFirstDay}}}|<<option txtCalFirstDay>>|(Monday = 0, Sunday = 6)|
|''First day of weekend:''<br>{{{config.options.txtCalStartOfWeekend}}}|<<option txtCalStartOfWeekend>>|(Monday = 0, Sunday = 6)|
<<option chkDisplayWeekNumbers>> Display week numbers //(note: Monday will be used as the start of the week)//
|''Week number display format:''<br>{{{config.options.txtWeekNumberDisplayFormat }}}|<<option txtWeekNumberDisplayFormat >>|
|''Week number link format:''<br>{{{config.options.txtWeekNumberLinkFormat }}}|<<option txtWeekNumberLinkFormat >>|
<<<
!!!!!Revisions
<<<
2008.02.27: in handler(), DON'T set hard-coded default date format, so that *customized* value (pre-defined in config.macros.calendar.journalDateFmt is used.
2008.02.17: in createCalendarYear(), fix next/previous year calculation (use parseInt() to convert to numeric value). Also, use journalDateFmt for date linking when NOT using [[DatePlugin]].
2008.02.16: in createCalendarDay(), week numbers now created as TiddlyLinks, allowing quick creation/navigation to 'weekly' journals (based on request from Kashgarinn)
2008.01.08: in createCalendarMonthHeader(), "month year" heading is now created as TiddlyLink, allowing quick creation/navigation to 'month-at-a-time' journals
2007.11.30: added "return false" to onclick handlers (prevent IE from opening blank pages)
2006.08.23: added handling for weeknumbers (code supplied by Martin Budden (see "wn**" comment marks). Also, incorporated updated by Jeremy Sheeley to add caching for reminders (see [[ReminderMacros]], if installed)
2005.10.30: in config.macros.calendar.handler(), use "tbody" element for IE compatibility. Also, fix year calculation for IE's getYear() function (which returns '2005' instead of '105'). Also, in createCalendarDays(), use showDate() function (see [[DatePlugin]], if installed) to render autostyled date with linked popup. Updated calendar stylesheet definition: use .calendar class-specific selectors, add text centering and margin settings
2006.05.29: added journalDateFmt handling
<<<
***/
/***
!!!!!Code section:
***/
//{{{
version.extensions.calendar = { major: 0, minor: 6, revision: 0, date: new Date(2008, 2, 27)};
if(config.options.txtCalFirstDay == undefined)
config.options.txtCalFirstDay = 0;
if(config.options.txtCalStartOfWeekend == undefined)
config.options.txtCalStartOfWeekend = 5;
if(config.options.chkDisplayWeekNumbers == undefined)//wn**
config.options.chkDisplayWeekNumbers = false;
if(config.options.chkDisplayWeekNumbers)
config.options.txtCalFirstDay = 0;
if(config.options.txtWeekNumberDisplayFormat == undefined)//wn**
config.options.txtWeekNumberDisplayFormat = "w0WW";
if(config.options.txtWeekNumberLinkFormat == undefined)//wn**
config.options.txtWeekNumberLinkFormat = "YYYY-w0WW";
config.macros.calendar = {};
config.macros.calendar.monthnames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.macros.calendar.daynames = ["M", "T", "W", "T", "F", "S", "S"];
config.macros.calendar.weekendbg = "#c0c0c0";
config.macros.calendar.monthbg = "#e0e0e0";
config.macros.calendar.holidaybg = "#ffc0c0";
config.macros.calendar.journalDateFmt = "DD MMM YYYY";
config.macros.calendar.monthdays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
config.macros.calendar.holidays = [ ]; // Not sure this is required anymore - use reminders instead
//}}}
//{{{
function calendarIsHoliday(date) // Is the given date a holiday?
{
var longHoliday = date.formatString("0DD/0MM/YYYY");
var shortHoliday = date.formatString("0DD/0MM");
for(var i = 0; i < config.macros.calendar.holidays.length; i++) {
if(config.macros.calendar.holidays[i] == longHoliday || config.macros.calendar.holidays[i] == shortHoliday)
return true;
}
return false;
}
//}}}
//{{{
config.macros.calendar.handler = function(place,macroName,params) {
var calendar = createTiddlyElement(place, "table", null, "calendar", null);
var tbody = createTiddlyElement(calendar, "tbody", null, null, null);
var today = new Date();
var year = today.getYear();
if (year<1900) year+=1900;
// get format for journal link by reading from SideBarOptions (ELS 5/29/06 - based on suggestion by Martin Budden)
var text = store.getTiddlerText("SideBarOptions");
var re = new RegExp("<<(?:newJournal)([^>]*)>>","mg"); var fm = re.exec(text);
if (fm && fm[1]!=null) { var pa=fm[1].readMacroParams(); if (pa[0]) this.journalDateFmt = pa[0]; }
if (params[0] == "thismonth") {
cacheReminders(new Date(year, today.getMonth(), 1, 0, 0), 31);
createCalendarOneMonth(tbody, year, today.getMonth());
}
else if (params[0] == "lastmonth") {
var month = today.getMonth()-1; if (month==-1) { month=11; year--; }
cacheReminders(new Date(year, month, 1, 0, 0), 31);
createCalendarOneMonth(tbody, year, month);
}
else if (params[0] == "nextmonth") {
var month = today.getMonth()+1; if (month>11) { month=0; year++; }
cacheReminders(new Date(year, month, 1, 0, 0), 31);
createCalendarOneMonth(tbody, year, month);
} else {
if (params[0]) year = params[0];
if(params[1]) {
cacheReminders(new Date(year, params[1]-1, 1, 0, 0), 31);
createCalendarOneMonth(tbody, year, params[1]-1);
} else {
cacheReminders(new Date(year, 0, 1, 0, 0), 366);
createCalendarYear(tbody, year);
}
}
window.reminderCacheForCalendar = null;
}
//}}}
//{{{
//This global variable is used to store reminders that have been cached
//while the calendar is being rendered. It will be renulled after the calendar is fully rendered.
window.reminderCacheForCalendar = null;
//}}}
//{{{
function cacheReminders(date, leadtime)
{
if (window.findTiddlersWithReminders == null) return;
window.reminderCacheForCalendar = {};
var leadtimeHash = [];
leadtimeHash [0] = 0;
leadtimeHash [1] = leadtime;
var t = findTiddlersWithReminders(date, leadtimeHash, null, 1);
for(var i = 0; i < t.length; i++) {
//just tag it in the cache, so that when we're drawing days, we can bold this one.
window.reminderCacheForCalendar[t[i]["matchedDate"]] = "reminder:" + t[i]["params"]["title"];
}
}
//}}}
//{{{
function createCalendarOneMonth(calendar, year, mon)
{
var row = createTiddlyElement(calendar, "tr", null, null, null);
createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, true, year, mon);
row = createTiddlyElement(calendar, "tr", null, null, null);
createCalendarDayHeader(row, 1);
createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarMonth(calendar, year, mon)
{
var row = createTiddlyElement(calendar, "tr", null, null, null);
createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, false, year, mon);
row = createTiddlyElement(calendar, "tr", null, null, null);
createCalendarDayHeader(row, 1);
createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarYear(calendar, year)
{
var row;
row = createTiddlyElement(calendar, "tr", null, null, null);
var back = createTiddlyElement(row, "td", null, null, null);
var backHandler = function() {
removeChildren(calendar);
createCalendarYear(calendar, parseInt(year)-1);
return false; // consume click
};
createTiddlyButton(back, "<", "Previous year", backHandler);
back.align = "center";
var yearHeader = createTiddlyElement(row, "td", null, "calendarYear", year);
yearHeader.align = "center";
yearHeader.setAttribute("colSpan",config.options.chkDisplayWeekNumbers?22:19);//wn**
var fwd = createTiddlyElement(row, "td", null, null, null);
var fwdHandler = function() {
removeChildren(calendar);
createCalendarYear(calendar, parseInt(year)+1);
return false; // consume click
};
createTiddlyButton(fwd, ">", "Next year", fwdHandler);
fwd.align = "center";
createCalendarMonthRow(calendar, year, 0);
createCalendarMonthRow(calendar, year, 3);
createCalendarMonthRow(calendar, year, 6);
createCalendarMonthRow(calendar, year, 9);
}
//}}}
//{{{
function createCalendarMonthRow(cal, year, mon)
{
var row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon], false, year, mon);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+1], false, year, mon);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+2], false, year, mon);
row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarDayHeader(row, 3);
createCalendarDayRows(cal, year, mon);
}
//}}}
//{{{
function createCalendarMonthHeader(cal, row, name, nav, year, mon)
{
var month;
if (nav) {
var back = createTiddlyElement(row, "td", null, null, null);
back.align = "center";
back.style.background = config.macros.calendar.monthbg;
var backMonHandler = function() {
var newyear = year;
var newmon = mon-1;
if(newmon == -1) { newmon = 11; newyear = newyear-1;}
removeChildren(cal);
cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);
createCalendarOneMonth(cal, newyear, newmon);
return false; // consume click
};
createTiddlyButton(back, "<", "Previous month", backMonHandler);
month = createTiddlyElement(row, "td", null, "calendarMonthname")
createTiddlyLink(month,name,true);
month.setAttribute("colSpan", config.options.chkDisplayWeekNumbers?6:5);//wn**
var fwd = createTiddlyElement(row, "td", null, null, null);
fwd.align = "center";
fwd.style.background = config.macros.calendar.monthbg;
var fwdMonHandler = function() {
var newyear = year;
var newmon = mon+1;
if(newmon == 12) { newmon = 0; newyear = newyear+1;}
removeChildren(cal);
cacheReminders(new Date(newyear, newmon , 1, 0, 0), 31);
createCalendarOneMonth(cal, newyear, newmon);
return false; // consume click
};
createTiddlyButton(fwd, ">", "Next month", fwdMonHandler);
} else {
month = createTiddlyElement(row, "td", null, "calendarMonthname", name)
month.setAttribute("colSpan",config.options.chkDisplayWeekNumbers?8:7);//wn**
}
month.align = "center";
month.style.background = config.macros.calendar.monthbg;
}
//}}}
//{{{
function createCalendarDayHeader(row, num)
{
var cell;
for(var i = 0; i < num; i++) {
if (config.options.chkDisplayWeekNumbers) createTiddlyElement(row, "td");//wn**
for(var j = 0; j < 7; j++) {
var d = j + (config.options.txtCalFirstDay - 0);
if(d > 6) d = d - 7;
cell = createTiddlyElement(row, "td", null, null, config.macros.calendar.daynames[d]);
if(d == (config.options.txtCalStartOfWeekend-0) || d == (config.options.txtCalStartOfWeekend-0+1))
cell.style.background = config.macros.calendar.weekendbg;
}
}
}
//}}}
//{{{
function createCalendarDays(row, col, first, max, year, mon) {
var i;
if (config.options.chkDisplayWeekNumbers){
if (first<=max) {
var ww = new Date(year,mon,first);
var td=createTiddlyElement(row, "td");//wn**
var link=createTiddlyLink(td,ww.formatString(config.options.txtWeekNumberLinkFormat),false);
link.appendChild(document.createTextNode(ww.formatString(config.options.txtWeekNumberDisplayFormat)));
}
else createTiddlyElement(row, "td", null, null, null);//wn**
}
for(i = 0; i < col; i++)
createTiddlyElement(row, "td", null, null, null);
var day = first;
for(i = col; i < 7; i++) {
var d = i + (config.options.txtCalFirstDay - 0);
if(d > 6) d = d - 7;
var daycell = createTiddlyElement(row, "td", null, null, null);
var isaWeekend = ((d == (config.options.txtCalStartOfWeekend-0) || d == (config.options.txtCalStartOfWeekend-0+1))? true:false);
if(day > 0 && day <= max) {
var celldate = new Date(year, mon, day);
// ELS 2005.10.30: use <<date>> macro's showDate() function to create popup
// ELS 5/29/06 - use journalDateFmt
if (window.showDate)
showDate(daycell,celldate,"popup","DD",config.macros.calendar.journalDateFmt,true, isaWeekend);
else {
if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;
var title = celldate.formatString(config.macros.calendar.journalDateFmt);
if(calendarIsHoliday(celldate))
daycell.style.background = config.macros.calendar.holidaybg;
if(window.findTiddlersWithReminders == null) {
var link = createTiddlyLink(daycell, title, false);
link.appendChild(document.createTextNode(day));
} else
var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);
}
}
day++;
}
}
//}}}
//{{{
// We've clicked on a day in a calendar - create a suitable pop-up of options.
// The pop-up should contain:
// * a link to create a new entry for that date
// * a link to create a new reminder for that date
// * an <hr>
// * the list of reminders for that date
// NOTE: The following code is only used when [[DatePlugin]] is not present
function onClickCalendarDate(e)
{
var button = this;
var date = button.getAttribute("title");
var dat = new Date(date.substr(6,4), date.substr(3,2)-1, date.substr(0, 2));
date = dat.formatString(config.macros.calendar.journalDateFmt);
var popup = createTiddlerPopup(this);
popup.appendChild(document.createTextNode(date));
var newReminder = function() {
var t = store.getTiddlers(date);
displayTiddler(null, date, 2, null, null, false, false);
if(t) {
document.getElementById("editorBody" + date).value += "\n<<reminder day:" + dat.getDate() +
" month:" + (dat.getMonth()+1) + " year:" + (dat.getYear()+1900) + " title: >>";
} else {
document.getElementById("editorBody" + date).value = "<<reminder day:" + dat.getDate() +
" month:" + (dat.getMonth()+1) +" year:" + (dat.getYear()+1900) + " title: >>";
}
return false; // consume click
};
var link = createTiddlyButton(popup, "New reminder", null, newReminder);
popup.appendChild(document.createElement("hr"));
var t = findTiddlersWithReminders(dat, [0,14], null, 1);
for(var i = 0; i < t.length; i++) {
link = createTiddlyLink(popup, t[i].tiddler, false);
link.appendChild(document.createTextNode(t[i].tiddler));
}
return false; // consume click
}
//}}}
//{{{
function calendarMaxDays(year, mon)
{
var max = config.macros.calendar.monthdays[mon];
if(mon == 1 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) max++;
return max;
}
//}}}
//{{{
function createCalendarDayRows(cal, year, mon)
{
var row = createTiddlyElement(cal, "tr", null, null, null);
var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first1 < 0) first1 = first1 + 7;
var day1 = -first1 + 1;
var first2 = (new Date(year, mon+1, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first2 < 0) first2 = first2 + 7;
var day2 = -first2 + 1;
var first3 = (new Date(year, mon+2, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first3 < 0) first3 = first3 + 7;
var day3 = -first3 + 1;
var max1 = calendarMaxDays(year, mon);
var max2 = calendarMaxDays(year, mon+1);
var max3 = calendarMaxDays(year, mon+2);
while(day1 <= max1 || day2 <= max2 || day3 <= max3) {
row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
createCalendarDays(row, 0, day2, max2, year, mon+1); day2 += 7;
createCalendarDays(row, 0, day3, max3, year, mon+2); day3 += 7;
}
}
//}}}
//{{{
function createCalendarDayRowsSingle(cal, year, mon)
{
var row = createTiddlyElement(cal, "tr", null, null, null);
var first1 = (new Date(year, mon, 1)).getDay() -1 - (config.options.txtCalFirstDay-0);
if(first1 < 0) first1 = first1+ 7;
var day1 = -first1 + 1;
var max1 = calendarMaxDays(year, mon);
while(day1 <= max1) {
row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
}
}
//}}}
//{{{
setStylesheet(".calendar, .calendar table, .calendar th, .calendar tr, .calendar td { text-align:center; } .calendar, .calendar a { margin:0px !important; padding:0px !important; }", "calendarStyles");
//}}}
/***
|''Name:''|CloseUnsavedOnCancel|
|''Sourse''|http://jackparke.googlepages.com/jtw.html#CloseUnsavedOnCancel|
|''Version:''|2.0.8 (16-Apr-2006)|
|''Author:''|SimonBaird|
|''Adapted By:''|[[Jack]]|
|''Type:''|Plugin|
!Description
When you click new tiddler then click cancel I think the new tiddler should close automatically. This plugin implements that behavious.
!Revision History
* 1.0.1 (11-Oct-2005) by SimonBaird
* 2.0.8 Made 2.0.x compatible by Jack on 16-Apr-2006
!Code
***/
//{{{
config.commands.cancelTiddler.handler = function(event,src,title) {
if(story.hasChanges(title) && !readOnly)
if(!confirm(this.warning.format([title])))
return false;
story.setDirty(title,false);
if (!store.tiddlerExists(title) || store.fetchTiddler(title).modifier==config.views.wikified.defaultModifier) {
story.closeTiddler(title,false);
store.removeTiddler(title)
} else {
story.displayTiddler(null,title);
}
return false;
}
//}}}
/***
|''Name:''|CryptoFunctionsPlugin|
|''Description:''|Support for cryptographic functions|
***/
//{{{
if(!version.extensions.CryptoFunctionsPlugin) {
version.extensions.CryptoFunctionsPlugin = {installed:true};
//--
//-- Crypto functions and associated conversion routines
//--
// Crypto "namespace"
function Crypto() {}
// Convert a string to an array of big-endian 32-bit words
Crypto.strToBe32s = function(str)
{
var be = Array();
var len = Math.floor(str.length/4);
var i, j;
for(i=0, j=0; i<len; i++, j+=4) {
be[i] = ((str.charCodeAt(j)&0xff) << 24)|((str.charCodeAt(j+1)&0xff) << 16)|((str.charCodeAt(j+2)&0xff) << 8)|(str.charCodeAt(j+3)&0xff);
}
while (j<str.length) {
be[j>>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
j++;
}
return be;
};
// Convert an array of big-endian 32-bit words to a string
Crypto.be32sToStr = function(be)
{
var str = "";
for(var i=0;i<be.length*32;i+=8)
str += String.fromCharCode((be[i>>5]>>>(24-i%32)) & 0xff);
return str;
};
// Convert an array of big-endian 32-bit words to a hex string
Crypto.be32sToHex = function(be)
{
var hex = "0123456789ABCDEF";
var str = "";
for(var i=0;i<be.length*4;i++)
str += hex.charAt((be[i>>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
return str;
};
// Return, in hex, the SHA-1 hash of a string
Crypto.hexSha1Str = function(str)
{
return Crypto.be32sToHex(Crypto.sha1Str(str));
};
// Return the SHA-1 hash of a string
Crypto.sha1Str = function(str)
{
return Crypto.sha1(Crypto.strToBe32s(str),str.length);
};
// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
Crypto.sha1 = function(x,blen)
{
// Add 32-bit integers, wrapping at 32 bits
add32 = function(a,b)
{
var lsw = (a&0xFFFF)+(b&0xFFFF);
var msw = (a>>16)+(b>>16)+(lsw>>16);
return (msw<<16)|(lsw&0xFFFF);
};
// Add five 32-bit integers, wrapping at 32 bits
add32x5 = function(a,b,c,d,e)
{
var lsw = (a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
var msw = (a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
return (msw<<16)|(lsw&0xFFFF);
};
// Bitwise rotate left a 32-bit integer by 1 bit
rol32 = function(n)
{
return (n>>>31)|(n<<1);
};
var len = blen*8;
// Append padding so length in bits is 448 mod 512
x[len>>5] |= 0x80 << (24-len%32);
// Append length
x[((len+64>>9)<<4)+15] = len;
var w = Array(80);
var k1 = 0x5A827999;
var k2 = 0x6ED9EBA1;
var k3 = 0x8F1BBCDC;
var k4 = 0xCA62C1D6;
var h0 = 0x67452301;
var h1 = 0xEFCDAB89;
var h2 = 0x98BADCFE;
var h3 = 0x10325476;
var h4 = 0xC3D2E1F0;
for(var i=0;i<x.length;i+=16) {
var j,t;
var a = h0;
var b = h1;
var c = h2;
var d = h3;
var e = h4;
for(j = 0;j<16;j++) {
w[j] = x[i+j];
t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=16;j<20;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),d^(b&(c^d)),w[j],k1);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=20;j<40;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k2);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=40;j<60;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),(b&c)|(d&(b|c)),w[j],k3);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
for(j=60;j<80;j++) {
w[j] = rol32(w[j-3]^w[j-8]^w[j-14]^w[j-16]);
t = add32x5(e,(a>>>27)|(a<<5),b^c^d,w[j],k4);
e=d; d=c; c=(b>>>2)|(b<<30); b=a; a = t;
}
h0 = add32(h0,a);
h1 = add32(h1,b);
h2 = add32(h2,c);
h3 = add32(h3,d);
h4 = add32(h4,e);
}
return Array(h0,h1,h2,h3,h4);
};
}
//}}}
Some notes on setting up and interrogating DCM's
''DCM and 2x2 factorial''
http://jiscmail.ac.uk/cgi-bin/webadmin?A2=ind0804&L=SPM&P=R4480&I=-3
Dear Marco,
As a precursor, the goal of the analysis in the papers by [[Heim et al., Human Brain Mapping, in press|http://www.ncbi.nlm.nih.gov/entrez/utils/fref.fcgi?PrId=3058&itool=Citation-def&uid=18095285&db=pubmed&url=http://dx.doi.org/10.1002/hbm.20512]], and by [[Stephan et al., J. Biosci., 2007|http://www.ncbi.nlm.nih.gov/pubmed/17426386?dopt=Citation]] is the same, i.e. to explain the occurrence of a two-way interaction in a particular region in terms of modulation of afferent connectivity.
I don't know what your exact SPM results look like, but here are two options how you could implement the same kind of analysis given that you have found a significant interaction (STIMULUS x TASK) in a region Y:
"""(a) If your SPM analysis shows a significant main effect of STIMULUS in region X, then you can test whether you can explain the interaction in region Y by driving region X with stim1 and stim2 separately (or with a single input encoding their difference) and modulating the connection X-->Y by task1 and task2 separately (or by a single input encoding their difference)."""
"""(b) If your SPM analysis fails to show a significant main effect of STIMULUS anywhere, but you find a region X responding to all stimuli alike, then you can test whether you can explain the interaction in region Y by driving region X with a single input encoding all stimuli and modulating the connection X-->Y by an input encoding the STIM x TASK interaction."""
In summary, note that the question is not necessarily about whether the connection is modulated by the interaction; this depends on the nature of the response in the source region. In contrast, the question is whether you can explain, in terms of task-dependent changes in connectivity, the occurrence of an interaction in a target region.
Should anything still be unclear, have a second look at the two papers mentioned above; the rationale underlying the analysis is described in detail there.
Best wishes,
Klaas
> Question from Marco Tettamanti
Dear Klaas and SPMers,
I am trying to setup a convenient DCM model for a 2x2 factorial design,
and I would very much appreciate your advice to be sure that what I am
doing is correct.
The model is constituted by 4 anatomical regions, with only one region
receiving a driving input.
There are two different questions that I would like to address:
1. Is the overall functional integration within the system stronger for stimulus A vs B (main effect of stimulus (A1+A2)-(B1+B2))?
2. Is there a significant interaction between the two factors, such that the connection strengths for stimulus A vs B are stronger in the context of task 1 vs 2 (interaction """(A1-B1)-(A2-B2))"""?
To address these two questions I have set up a GLM Design matrix with two conditions:
- onset of events of condition A (representing the main effect """(A1+A2)-(B1+B2)) -> 'STIMULUS'"""
- onsent of events of condition 1 (representing the main effect """(A1+B1)-(A2+B2)) -> 'TASK'"""
I then defined a DCM model, in which 'STIMULUS' provides the driving input, and 'TASK' is allowed to modulate all the defined intrinsic connections.
Is the procedure described above so far correct?
Do I understand it correctly that:
- The intrinsic connection strengths (matrix DCM.A) represent how the main effect of 'STIMULUS' that is induced in the input area is conveyed, via the specified connections, to other areas (answer to question 1 above). - The modulatory connection strengths (matrix DCM.B) represent the degree to which the activity is passed from the input region to other areas, depending on the level of 'TASK' (answer to question 2 above).
Thank you for your help!
Best wishes,
Marco
----
''Adjusting data from VOI's''
http://jiscmail.ac.uk/cgi-bin/webadmin?A2=ind0803&L=SPM&P=R16361&I=-3
Dear Jane,
When you extract data using the eigenvariate button (formerly known as "VOI" button), you only get the option to adjust your data with regard to any F-contrasts that have already been defined (and computed). If no F-contrast is found in the present directory, SPM will automatically choose "<don't adjust>".
Best wishes
Klaas
http://jiscmail.ac.uk/cgi-bin/webadmin?A2=ind0802&L=SPM&P=R8425&I=-3
Dear Christian,
Generally,
I would recommend to adjust your extracted timeseries with regard to an F contrast for the effects of interest (i.e. excluding the mean and potential confounds like movement parameters). Then your problem should not occur, even with the piece of code you are using now.
Best wishes,
Klaas
----
''Combining DCM data from different sessions or runs''
http://jiscmail.ac.uk/cgi-bin/webadmin?A2=ind0802&L=SPM&P=R24221&I=-3
Dear Tobias,
There are two ways in which you could proceed without having to re-estimate your GLMs:
1. For each subject, you could combine the parameter estimates across sessions using the "average" function in DCM. For each parameter estimate, this will give you a weighted mean across sessions (weighted by the session-specific posterior variances). You could then feed the resulting values into a classical second level analysis (e.g. t-test).
2. You can account for the dependency amongst the parameter estimates within each subject by using a repeated measures ANOVA ("repeated measures" because you have multiple parameter estimates for each subject, i.e. one for each session).
All the very best,
Klaas
> Question from Tobias Overrath
Hi Klaas,
I hope you are enjoying your life together in Zurich - I am very
happy here in Princeton, sharing life with Lillian at last.
I have a hopefully quick question regarding multiple sessions in DCM. I understand one way is to concatenate the session (and introduce session constants etc.); however, I would rather avoid that (mainly for time reasons, i.e. having to re-estimate the GLMs). Instead, I have estimated each model for each session separately (e.g. DCM_model1_sess1, DCM_model1_sess2, DCM_model1_sess3, DCM_model1_sess4). I have searched in the SPM archives and in your Powerpoint slides to find a way how to combine the different session DCMs of the same model?
Or would you suggest I bite the bullet and re-estimate my GLMs with concatenated sessions?
Best,
Tobias
----
''DCM threshold''
http://jiscmail.ac.uk/cgi-bin/webadmin?A2=ind0804&L=SPM&P=R4623&I=-3
Dear Mina,
...the relation between coupling strength and half-life of the neuronal process is (ln 2) / t, NOT ln(2/t). Thus, using the default treshold of zero for coupling strength means that t = infinity, i.e. you want to test for the probability that the neuronal process is happening at all. This is the most generic question you could ask and does not force you to make any assumptions. For standard situations, I would generally recommend to use this default threshold.
Best wishes,
Klaas
----
/***
|Name|DatePlugin|
|Source|http://www.TiddlyTools.com/#DatePlugin|
|Documentation|http://www.TiddlyTools.com/#DatePluginInfo|
|Version|2.7.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|formatted dates plus popup menu with 'journal' link, changes and (optional) reminders|
There are quite a few calendar generators, reminders, to-do lists, 'dated tiddlers' journals, blog-makers and GTD-like schedule managers that have been built around TW. While they all have different purposes, and vary in format, interaction, and style, in one way or another each of these plugins displays and/or uses date-based information to make finding, accessing and managing relevant tiddlers easier. This plugin provides a general approach to embedding dates and date-based links/menus within tiddler content.
!!!!!Documentation
>see [[DatePluginInfo]]
!!!!!Configuration
>see [[DatePluginConfig]]
!!!!!Revisions
<<<
2008.03.08 [2.7.0] in addModifiedsToPopup(), if a tiddler was created on the specified date, don't list it in the 'changed' section of the popup. Based on a request from Kashgarinn.
|please see [[DatePluginInfo]] for additional revision details|
2005.10.30 [0.9.0] pre-release
<<<
!!!!!Code
***/
//{{{
version.extensions.date = {major: 2, minor: 7, revision: 0, date: new Date(2008,3,8)};
config.macros.date = {
format: "YYYY.0MM.0DD", // default date display format
linkformat: "YYYY.0MM.0DD", // 'dated tiddler' link format
linkedbg: "#babb1e", // "babble"
todaybg: "#ffab1e", // "fable"
weekendbg: "#c0c0c0", // "cocoa"
holidaybg: "#ffaace", // "face"
createdbg: "#bbeeff", // "beef"
modifiedsbg: "#bbeeff", // "beef"
remindersbg: "#c0ffee", // "coffee"
holidays: [ "01/01", "07/04", "07/24", "11/24" ], // NewYearsDay, IndependenceDay(US), Eric's Birthday (hooray!), Thanksgiving(US)
weekend: [ 1,0,0,0,0,0,1 ] // [ day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6 ]
};
config.macros.date.handler = function(place,macroName,params)
{
// do we want to see a link, a popup, or just a formatted date?
var mode="display";
if (params[0]=="display") { mode=params[0]; params.shift(); }
if (params[0]=="popup") { mode=params[0]; params.shift(); }
if (params[0]=="link") { mode=params[0]; params.shift(); }
// get the date
var now = new Date();
var date = now;
if (!params[0] || params[0]=="today")
{ params.shift(); }
else if (params[0]=="filedate")
{ date=new Date(document.lastModified); params.shift(); }
else if (params[0]=="tiddler")
{ date=store.getTiddler(story.findContainingTiddler(place).id.substr(7)).modified; params.shift(); }
else if (params[0].substr(0,8)=="tiddler:")
{ var t; if ((t=store.getTiddler(params[0].substr(8)))) date=t.modified; params.shift(); }
else {
var y = eval(params.shift().replace(/Y/ig,(now.getYear()<1900)?now.getYear()+1900:now.getYear()));
var m = eval(params.shift().replace(/M/ig,now.getMonth()+1));
var d = eval(params.shift().replace(/D/ig,now.getDate()+0));
date = new Date(y,m-1,d);
}
// date format with optional custom override
var format=this.format; if (params[0]) format=params.shift();
var linkformat=this.linkformat; if (params[0]) linkformat=params.shift();
showDate(place,date,mode,format,linkformat);
}
window.showDate=showDate;
function showDate(place,date,mode,format,linkformat,autostyle,weekend)
{
if (!mode) mode="display";
if (!format) format=config.macros.date.format;
if (!linkformat) linkformat=config.macros.date.linkformat;
if (!autostyle) autostyle=false;
// format the date output
var title = date.formatString(format);
var linkto = date.formatString(linkformat);
// just show the formatted output
if (mode=="display") { place.appendChild(document.createTextNode(title)); return; }
// link to a 'dated tiddler'
var link = createTiddlyLink(place, linkto, false);
link.appendChild(document.createTextNode(title));
link.title = linkto;
link.date = date;
link.format = format;
link.linkformat = linkformat;
// if using a popup menu, replace click handler for dated tiddler link
// with handler for popup and make link text non-italic (i.e., an 'existing link' look)
if (mode=="popup") {
link.onclick = onClickDatePopup;
link.style.fontStyle="normal";
}
// format the popup link to show what kind of info it contains (for use with calendar generators)
if (autostyle) setDateStyle(place,link,weekend);
}
//}}}
//{{{
// NOTE: This function provides default logic for setting the date style when displayed in a calendar
// To customize the date style logic, please see[[DatePluginConfig]]
function setDateStyle(place,link,weekend) {
// alias variable names for code readability
var date=link.date;
var fmt=link.linkformat;
var linkto=date.formatString(fmt);
var cmd=config.macros.date;
if ((weekend!==undefined?weekend:isWeekend(date))&&(cmd.weekendbg!=""))
{ place.style.background = cmd.weekendbg; }
if (hasModifieds(date)||hasCreateds(date)||hasTagged(date,fmt))
{ link.style.fontStyle="normal"; link.style.fontWeight="bold"; }
if (hasReminders(date))
{ link.style.textDecoration="underline"; }
if (isToday(date))
{ link.style.border="1px solid black"; }
if (isHoliday(date)&&(cmd.holidaybg!=""))
{ place.style.background = cmd.holidaybg; }
if (hasCreateds(date)&&(cmd.createdbg!=""))
{ place.style.background = cmd.createdbg; }
if (hasModifieds(date)&&(cmd.modifiedsbg!=""))
{ place.style.background = cmd.modifiedsbg; }
if ((hasTagged(date,fmt)||store.tiddlerExists(linkto))&&(cmd.linkedbg!=""))
{ place.style.background = cmd.linkedbg; }
if (hasReminders(date)&&(cmd.remindersbg!=""))
{ place.style.background = cmd.remindersbg; }
if (isToday(date)&&(cmd.todaybg!=""))
{ place.style.background = cmd.todaybg; }
}
//}}}
//{{{
function isToday(date) // returns true if date is today
{ var now=new Date(); return ((now-date>=0) && (now-date<86400000)); }
function isWeekend(date) // returns true if date is a weekend
{ return (config.macros.date.weekend[date.getDay()]); }
function isHoliday(date) // returns true if date is a holiday
{
var longHoliday = date.formatString("0MM/0DD/YYYY");
var shortHoliday = date.formatString("0MM/0DD");
for(var i = 0; i < config.macros.date.holidays.length; i++) {
var holiday=config.macros.date.holidays[i];
if (holiday==longHoliday||holiday==shortHoliday) return true;
}
return false;
}
//}}}
//{{{
// Event handler for clicking on a day popup
function onClickDatePopup(e)
{
if (!e) var e = window.event;
var theTarget = resolveTarget(e);
var popup = Popup.create(this);
if(popup) {
// always show dated tiddler link (or just date, if readOnly) at the top...
if (!readOnly || store.tiddlerExists(this.date.formatString(this.linkformat)))
createTiddlyLink(popup,this.date.formatString(this.linkformat),true);
else
createTiddlyText(popup,this.date.formatString(this.linkformat));
if (!config.options.chkDatePopupHideCreated)
addCreatedsToPopup(popup,this.date,this.format);
if (!config.options.chkDatePopupHideChanged)
addModifiedsToPopup(popup,this.date,this.format);
if (!config.options.chkDatePopupHideTagged)
addTaggedToPopup(popup,this.date,this.linkformat);
if (!config.options.chkDatePopupHideReminders)
addRemindersToPopup(popup,this.date,this.linkformat);
}
Popup.show(popup,false);
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
return(false);
}
//}}}
//{{{
function indexCreateds() // build list of tiddlers, hash indexed by creation date
{
var createds= { };
var tiddlers = store.getTiddlers("title","excludeLists");
for (var t = 0; t < tiddlers.length; t++) {
var date = tiddlers[t].created.formatString("YYYY0MM0DD")
if (!createds[date])
createds[date]=new Array();
createds[date].push(tiddlers[t].title);
}
return createds;
}
function hasCreateds(date) // returns true if date has created tiddlers
{
if (!config.macros.date.createds) config.macros.date.createds=indexCreateds();
return (config.macros.date.createds[date.formatString("YYYY0MM0DD")]!=undefined);
}
function addCreatedsToPopup(popup,when,format)
{
var force=(store.isDirty() && when.formatString("YYYY0MM0DD")==new Date().formatString("YYYY0MM0DD"));
if (force || !config.macros.date.createds) config.macros.date.createds=indexCreateds();
var indent=String.fromCharCode(160)+String.fromCharCode(160);
var createds = config.macros.date.createds[when.formatString("YYYY0MM0DD")];
if (createds) {
createds.sort();
var e=createTiddlyElement(popup,"div",null,null,"created ("+createds.length+")");
for(var t=0; t<createds.length; t++) {
var link=createTiddlyLink(popup,createds[t],false);
link.appendChild(document.createTextNode(indent+createds[t]));
createTiddlyElement(popup,"br",null,null,null);
}
}
}
//}}}
//{{{
function indexModifieds() // build list of tiddlers, hash indexed by modification date
{
var modifieds= { };
var tiddlers = store.getTiddlers("title","excludeLists");
for (var t = 0; t < tiddlers.length; t++) {
var date = tiddlers[t].modified.formatString("YYYY0MM0DD")
if (!modifieds[date])
modifieds[date]=new Array();
modifieds[date].push(tiddlers[t].title);
}
return modifieds;
}
function hasModifieds(date) // returns true if date has modified tiddlers
{
if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();
return (config.macros.date.modifieds[date.formatString("YYYY0MM0DD")]!=undefined);
}
function addModifiedsToPopup(popup,when,format)
{
var date=when.formatString("YYYY0MM0DD");
var force=(store.isDirty() && date==new Date().formatString("YYYY0MM0DD"));
if (force || !config.macros.date.modifieds) config.macros.date.modifieds=indexModifieds();
var indent=String.fromCharCode(160)+String.fromCharCode(160);
var mods = config.macros.date.modifieds[date];
if (mods) {
// if a tiddler was created on this date, don't list it in the 'changed' section
if (config.macros.date.createds && config.macros.date.createds[date]) {
var temp=[];
for(var t=0; t<mods.length; t++)
if (!config.macros.date.createds[date].contains(mods[t]))
temp.push(mods[t]);
mods=temp;
}
mods.sort();
var e=createTiddlyElement(popup,"div",null,null,"changed ("+mods.length+")");
for(var t=0; t<mods.length; t++) {
var link=createTiddlyLink(popup,mods[t],false);
link.appendChild(document.createTextNode(indent+mods[t]));
createTiddlyElement(popup,"br",null,null,null);
}
}
}
//}}}
//{{{
function hasTagged(date,format) // returns true if date is tagging other tiddlers
{
return store.getTaggedTiddlers(date.formatString(format)).length>0;
}
function addTaggedToPopup(popup,when,format)
{
var indent=String.fromCharCode(160)+String.fromCharCode(160);
var tagged=store.getTaggedTiddlers(when.formatString(format));
if (tagged.length) var e=createTiddlyElement(popup,"div",null,null,"tagged ("+tagged.length+")");
for(var t=0; t<tagged.length; t++) {
var link=createTiddlyLink(popup,tagged[t].title,false);
link.appendChild(document.createTextNode(indent+tagged[t].title));
createTiddlyElement(popup,"br",null,null,null);
}
}
//}}}
//{{{
function indexReminders(date,leadtime) // build list of tiddlers with reminders, hash indexed by reminder date
{
var reminders = { };
if(window.findTiddlersWithReminders!=undefined) { // reminder plugin is installed
// DEBUG var starttime=new Date();
var t = findTiddlersWithReminders(date, [0,leadtime], null, null, 1);
for(var i=0; i<t.length; i++) reminders[t[i].matchedDate]=true;
// DEBUG var out="Found "+t.length+" reminders in "+((new Date())-starttime+1)+"ms\n";
// DEBUG out+="startdate: "+date.toLocaleDateString()+"\n"+"leadtime: "+leadtime+" days\n\n";
// DEBUG for(var i=0; i<t.length; i++) { out+=t[i].matchedDate.toLocaleDateString()+" "+t[i].params.title+"\n"; }
// DEBUG alert(out);
}
return reminders;
}
function hasReminders(date) // returns true if date has reminders
{
if (window.reminderCacheForCalendar)
return window.reminderCacheForCalendar[date]; // use calendar cache
if (!config.macros.date.reminders)
config.macros.date.reminders = indexReminders(date,90); // create a 90-day leadtime reminder cache
return (config.macros.date.reminders[date]);
}
function addRemindersToPopup(popup,when,format)
{
if(window.findTiddlersWithReminders==undefined) return; // reminder plugin not installed
var indent = String.fromCharCode(160)+String.fromCharCode(160);
var reminders=findTiddlersWithReminders(when, [0,31],null,null,1);
createTiddlyElement(popup,"div",null,null,"reminders ("+(reminders.length||"none")+")");
for(var t=0; t<reminders.length; t++) {
link = createTiddlyLink(popup,reminders[t].tiddler,false);
var diff=reminders[t].diff;
diff=(diff<1)?"Today":((diff==1)?"Tomorrow":diff+" days");
var txt=(reminders[t].params["title"])?reminders[t].params["title"]:reminders[t].tiddler;
link.appendChild(document.createTextNode(indent+diff+" - "+txt));
createTiddlyElement(popup,"br",null,null,null);
}
if (readOnly) return; // omit "new reminder..." link
var link = createTiddlyLink(popup,indent+"new reminder...",true); createTiddlyElement(popup,"br");
var title = when.formatString(format);
link.title="add a reminder to '"+title+"'";
link.onclick = function() {
// show tiddler editor
story.displayTiddler(null, title, 2, null, null, false, false);
// find body 'textarea'
var c =document.getElementById("tiddler" + title).getElementsByTagName("*");
for (var i=0; i<c.length; i++) if ((c[i].tagName.toLowerCase()=="textarea") && (c[i].getAttribute("edit")=="text")) break;
// append reminder macro to tiddler content
if (i<c.length) {
if (store.tiddlerExists(title)) c[i].value+="\n"; else c[i].value="";
c[i].value += "<<reminder";
c[i].value += " day:"+when.getDate();
c[i].value += " month:"+(when.getMonth()+1);
c[i].value += " year:"+when.getFullYear();
c[i].value += ' title:"Enter a title" >>';
}
};
}
//}}}
/***
|Name|DatePluginConfig|
|Source|http://www.TiddlyTools.com/#DatePluginConfig|
|Documentation|http://www.TiddlyTools.com/#DatePluginInfo|
|Version|2.6.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|formats, background colors and other optional settings for DatePlugin|
***/
// // Default popup content display options (can be overridden by cookies)
//{{{
if (config.options.chkDatePopupHideCreated===undefined)
config.options.chkDatePopupHideCreated=false;
if (config.options.chkDatePopupHideChanged===undefined)
config.options.chkDatePopupHideChanged=false;
if (config.options.chkDatePopupHideTagged===undefined)
config.options.chkDatePopupHideTagged=false;
if (config.options.chkDatePopupHideReminders===undefined)
config.options.chkDatePopupHideReminders=false;
//}}}
// // fixed-date annual holidays
//{{{
config.macros.date.holidays=[
"01/01", // NewYearsDay,
"07/04", // US Independence Day
"12/25", // Christmas
];
//}}}
// // weekend map (1=weekend, 0=weekday)
//{{{
config.macros.date.weekend=[ 1,0,0,0,0,0,1 ]; // day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6
//}}}
// // date display/link formats
//{{{
config.macros.date.format="YYYY.0MM.0DD"; // default date display format
config.macros.date.linkformat="YYYY.0MM.0DD"; // 'dated tiddler' link format
//}}}
// // When displaying a calendar (see [[CalendarPlugin]]), you can customize the colors/styles that are applied to the calendar dates by modifying the values and/or functions below:
//{{{
// default calendar colors
config.macros.date.weekendbg="#c0c0c0";
config.macros.date.holidaybg="#ffaace";
config.macros.date.createdbg="#bbeeff";
config.macros.date.modifiedsbg="#bbeeff";
config.macros.date.linkedbg="#babb1e";
config.macros.date.remindersbg="#c0ffee";
// apply calendar styles
function setDateStyle(place,link,weekend) {
// alias variable names for code readability
var date=link.date;
var fmt=link.linkformat;
var linkto=date.formatString(fmt);
var cmd=config.macros.date;
if ((weekend!==undefined?weekend:isWeekend(date))&&(cmd.weekendbg!=""))
{ place.style.background = cmd.weekendbg; }
if (hasModifieds(date)||hasCreateds(date)||hasTagged(date,fmt))
{ link.style.fontStyle="normal"; link.style.fontWeight="bold"; }
if (hasReminders(date))
{ link.style.textDecoration="underline"; }
if (isToday(date))
{ link.style.border="1px solid black"; }
if (isHoliday(date)&&(cmd.holidaybg!=""))
{ place.style.background = cmd.holidaybg; }
if (hasCreateds(date)&&(cmd.createdbg!=""))
{ place.style.background = cmd.createdbg; }
if (hasModifieds(date)&&(cmd.modifiedsbg!=""))
{ place.style.background = cmd.modifiedsbg; }
if ((hasTagged(date,fmt)||store.tiddlerExists(linkto))&&(cmd.linkedbg!=""))
{ place.style.background = cmd.linkedbg; }
if (hasReminders(date)&&(cmd.remindersbg!=""))
{ place.style.background = cmd.remindersbg; }
if (isToday(date)&&(cmd.todaybg!=""))
{ place.style.background = cmd.todaybg; }
}
//}}}
[[Neurology Brain Imaging Laboratory]]
[[News]]
/***
|''Name:''|DeprecatedFunctionsPlugin|
|''Description:''|Support for deprecated functions removed from core|
***/
//{{{
if(!version.extensions.DeprecatedFunctionsPlugin) {
version.extensions.DeprecatedFunctionsPlugin = {installed:true};
//--
//-- Deprecated code
//--
// @Deprecated: Use createElementAndWikify and this.termRegExp instead
config.formatterHelpers.charFormatHelper = function(w)
{
w.subWikify(createTiddlyElement(w.output,this.element),this.terminator);
};
// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead
config.formatterHelpers.monospacedByLineHelper = function(w)
{
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
createTiddlyElement(w.output,"pre",null,null,text);
w.nextMatch = lookaheadRegExp.lastIndex;
}
};
// @Deprecated: Use <br> or <br /> instead of <<br>>
config.macros.br = {};
config.macros.br.handler = function(place)
{
createTiddlyElement(place,"br");
};
// Find an entry in an array. Returns the array index or null
// @Deprecated: Use indexOf instead
Array.prototype.find = function(item)
{
var i = this.indexOf(item);
return i == -1 ? null : i;
};
// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed()
// @Deprecated: Use store.getLoader().internalizeTiddler instead
Tiddler.prototype.loadFromDiv = function(divRef,title)
{
return store.getLoader().internalizeTiddler(store,this,title,divRef);
};
// Format the text for storage in an HTML DIV
// @Deprecated Use store.getSaver().externalizeTiddler instead.
Tiddler.prototype.saveToDiv = function()
{
return store.getSaver().externalizeTiddler(store,this);
};
// @Deprecated: Use store.allTiddlersAsHtml() instead
function allTiddlersAsHtml()
{
return store.allTiddlersAsHtml();
}
// @Deprecated: Use refreshPageTemplate instead
function applyPageTemplate(title)
{
refreshPageTemplate(title);
}
// @Deprecated: Use story.displayTiddlers instead
function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3)
{
story.displayTiddlers(srcElement,titles,template,animate);
}
// @Deprecated: Use story.displayTiddler instead
function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3)
{
story.displayTiddler(srcElement,title,template,animate);
}
// @Deprecated: Use functions on right hand side directly instead
var createTiddlerPopup = Popup.create;
var scrollToTiddlerPopup = Popup.show;
var hideTiddlerPopup = Popup.remove;
// @Deprecated: Use right hand side directly instead
var regexpBackSlashEn = new RegExp("\\\\n","mg");
var regexpBackSlash = new RegExp("\\\\","mg");
var regexpBackSlashEss = new RegExp("\\\\s","mg");
var regexpNewLine = new RegExp("\n","mg");
var regexpCarriageReturn = new RegExp("\r","mg");
}
//}}}
Effective Connectivity Analyses.
1. The first step is to make a new design matrix with all the data collapsed into a single session.
a. To collapse vectors into a single session
b. - In the matlab window type vectorname = sort([vectornamesession1 vectornamesession2 vectornamesession3 etc]):
c. - Do this for all vectors
b. Follow general steps for making a design matrix but you will enter in number of scans as number of scans per run * number of runs. (e.g. 1360 for ip4taste instead of 272 272 272 272 272
c. When adding scans do them in order of protocol.
2. Define your source region.
a. This is the region from which data will be pulled to predict activity in other regions under different behavioral or sensory conditions. Go to the original design matrix (not the one session matrix) find the activation of interest. Write down the coordinates.
b. Go to the one session design matrix and make an F-contrast called effects of interest. This contrast should include all events of interest and their time derivatives.
i. 1
ii. 0 1
iii. 0 0 1
iv. 0 0 0 1
v. this would specify the first two events and their derivatives
vi. include as many events as are relevant for your ppi.
c. Display effects of interest and go the co-ordinate you wrote down and SVC to 15mm and go to significant coordinate
d. Hit VOI
e. name it – e.g. “OFC”
f. don’t adjust
g. sphere
h. rad = 15mm
3. hit PPI
a. select one session .mat file
b. pick psychphysiological interaction
c. select the VOI you just made
d. select variables to use and their contrast weights. (this is similar to making a t-contrast. If you are interested in comparing snt and snw vs randkt + randkw you say yes to only these variables and give weights of 1 to snt, 1 to snw, -1 to randkt and –1 to randkw).
e. name the PPI
f. a PPI will come up and you should print it to file.
4. Make a new directory with the onesession dir.– e.g. PPI_OFC
a. cd into it
b. type load (spm_get)
c. select ppi.mat file
d. hit fMRI design and go through normal steps
e. when you get to conditions = 0
f. usr specified regressors = 3
i. PPI.ppi
ii. PPI.Y
iii. PPI.P
g. Hit fMRI data and go through normal steps
5. Estimate the model and put a 1 in the first column to look at the effects of snt + snw vs randkt +randkw).
27/09/06 15:59
Question: How would differences in the number of partial trials affect the statistical analysis of event-related fMRI?
Answer: The use of partial trials has been examined by Ollinger et al., (Separating processes within a trial in event-related functional MRI: II. Analysis. ~NeuroImage, 2001. 13(1): p. 218-229 )[[Pubmed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=11133324&query_hl=1&itool=pubmed_docsum]]
Figure 3 from that paper is shown below. It shows mean variance, RMS variation of the variance and RMS correlation coefficient among points in the time course as a function of the number of partial trials. Better designs are those that decrease variance and point-to-point correlations. Notice that the variance decreases with increasing partial trials from about 20% to 40%. Ollinger et al remark that if the only consideration was variance than the optimum percent partial trials would be 40%. However, psychologically this would probably not be optimum. Looking at the curves, the amount of variance resches a minimum plateau around 20%-30%. Thus, partial trial fractions in this interval give psychologically and statistically reasonable designs. The article also cites Shulman et al 1999 [[Pubmed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=10531451&query_hl=5&itool=pubmed_DocSum]] and Corbetta et al., 2000 [[Pubmed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=10700263&query_hl=3&itool=pubmed_ExternalLink]] as showing that partial trial fractions from 20% - 25% are reasonable.
[img[Ollinger et al., Neuroimage, 2002|http://d.gitelman.googlepages.com/ollinger-ni-2001b.jpg]]
Like most wikis, TiddlyWiki supports a range of simplified character formatting:
| !To get | !Type this |
| ''Bold'' | {{{''Bold''}}} |
| ==Strikethrough== | {{{==Strikethrough==}}} |
| __Underline__ | {{{__Underline__}}} (that's two underline characters) |
| //Italic// | {{{//Italic//}}} |
| Superscript: 2^^3^^=8 | {{{2^^3^^=8}}} |
| Subscript: a~~ij~~ = -a~~ji~~ | {{{a~~ij~~ = -a~~ji~~}}} |
| @@highlight@@ | {{{@@highlight@@}}} |
| > blockquote | > 2 |
| no formatting | {{{use tags <nowiki> and </nowiki> or triple quotes on each side of the """text"""}}}|
----
To make quoted bits of text stand out, you can use ~BlockQuotes within your [[tiddler]]s, like this:
JeremyRuston said:
<<<
A TiddlyWiki is like a blog because it's divided up into neat little chunks, but it encourages you to read it by hyperlinking rather than sequentially: if you like, a non-linear blog analogue that binds the individual microcontent items into a cohesive whole.
<<<
To blockquote several lines of text, type {{{<<<}}} on the first line, the text on lines 2 - n, and {{{<<<}}} on line n+1.
Blockquotes can also have different levels.
>level 1: Type this {{{>}}} level 1
>>level 2: Type this {{{>>}}} level 2
>>>level 3: Type this {{{>>>}}} level 3
----
Bullet points use
*one or
**more asterisks {{{*}}}
***at the start of each line.
---
Numbered bullet points use
#one or
##more {{{#}}} signs
###at the start of each line.
----
A highlight can also accept CSS syntax to directly style the text:
@@color:green;green coloured@@
@@background-color:#ff0000;color:#ffffff;red coloured@@
@@text-shadow:black 3px 3px 8px;font-size:18pt;display:block;margin:1em 1em 1em 1em;border:1px solid black;Access any CSS style@@
<<<
//For backwards compatibility, the following highlight syntax is also accepted://
{{{
@@bgcolor(#ff0000):color(#ffffff):red coloured@@
}}}
@@bgcolor(#ff0000):color(#ffffff):red coloured@@
/***
|Name|FontSizePlugin|
|Created by|SaqImtiaz|
|Location|http://tw.lewcid.org/#FontSizePlugin|
|Version|1.0|
|Requires|~TW2.x|
!Description:
Resize tiddler text on the fly. The text size is remembered between sessions by use of a cookie.
You can customize the maximum and minimum allowed sizes.
(only affects tiddler content text, not any other text)
Also, you can load a TW file with a font-size specified in the url.
Eg: http://tw.lewcid.org/#font:110
!Demo:
Try using the font-size buttons in the sidebar, or in the MainMenu above.
!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
Then put {{{<<fontSize "font-size:">>}}} in your SideBarOptions tiddler, or anywhere else that you might like.
!Usage
{{{<<fontSize>>}}} results in <<fontSize>>
{{{<<fontSize font-size: >>}}} results in <<fontSize font-size:>>
!Customizing:
The buttons and prefix text are wrapped in a span with class fontResizer, for easy css styling.
To change the default font-size, and the maximum and minimum font-size allowed, edit the config.fontSize.settings section of the code below.
!Notes:
This plugin assumes that the initial font-size is 100% and then increases or decreases the size by 10%. This stepsize of 10% can also be customized.
!History:
*27-07-06, version 1.0 : prevented double clicks from triggering editing of containing tiddler.
*25-07-06, version 0.9
!Code
***/
//{{{
config.fontSize={};
//configuration settings
config.fontSize.settings =
{
defaultSize : 100, // all sizes in %
maxSize : 200,
minSize : 40,
stepSize : 10
};
//startup code
var fontSettings = config.fontSize.settings;
if (!config.options.txtFontSize)
{config.options.txtFontSize = fontSettings.defaultSize;
saveOptionCookie("txtFontSize");}
setStylesheet(".tiddler .viewer {font-size:"+config.options.txtFontSize+"%;}\n","fontResizerStyles");
setStylesheet("#contentWrapper .fontResizer .button {display:inline;font-size:105%; font-weight:bold; margin:0 1px; padding: 0 3px; text-align:center !important;}\n .fontResizer {margin:0 0.5em;}","fontResizerButtonStyles");
//macro
config.macros.fontSize={};
config.macros.fontSize.handler = function (place,macroName,params,wikifier,paramString,tiddler)
{
var sp = createTiddlyElement(place,"span",null,"fontResizer");
sp.ondblclick=this.onDblClick;
if (params[0])
createTiddlyText(sp,params[0]);
createTiddlyButton(sp,"+","increase font-size",this.incFont);
createTiddlyButton(sp,"=","reset font-size",this.resetFont);
createTiddlyButton(sp,"–","decrease font-size",this.decFont);
}
config.macros.fontSize.onDblClick = function (e)
{
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
return false;
}
config.macros.fontSize.setFont = function ()
{
saveOptionCookie("txtFontSize");
setStylesheet(".tiddler .viewer {font-size:"+config.options.txtFontSize+"%;}\n","fontResizerStyles");
}
config.macros.fontSize.incFont=function()
{
if (config.options.txtFontSize < fontSettings.maxSize)
config.options.txtFontSize = (config.options.txtFontSize*1)+fontSettings.stepSize;
config.macros.fontSize.setFont();
}
config.macros.fontSize.decFont=function()
{
if (config.options.txtFontSize > fontSettings.minSize)
config.options.txtFontSize = (config.options.txtFontSize*1) - fontSettings.stepSize;
config.macros.fontSize.setFont();
}
config.macros.fontSize.resetFont=function()
{
config.options.txtFontSize=fontSettings.defaultSize;
config.macros.fontSize.setFont();
}
config.paramifiers.font =
{
onstart: function(v)
{
config.options.txtFontSize = v;
config.macros.fontSize.setFont();
}
};
//}}}
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================
// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {
if (!window.abego) window.abego = {};
version.extensions.ForEachTiddlerPlugin = {
major: 1, minor: 0, revision: 8,
date: new Date(2007,3,12),
source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};
// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
TiddlyWiki.prototype.forEachTiddler = function(callback) {
for(var t in this.tiddlers) {
callback.call(this,t,this.tiddlers[t]);
}
};
}
//============================================================================
// forEachTiddler Macro
//============================================================================
version.extensions.forEachTiddler = {
major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
config.macros.forEachTiddler = {
// Standard Properties
label: "forEachTiddler",
prompt: "Perform actions on a (sorted) selection of tiddlers",
// actions
actions: {
addToList: {},
write: {}
}
};
// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------
config.macros.forEachTiddler.getContainingTiddler = function(e) {
while(e && !hasClass(e,"tiddler"))
e = e.parentNode;
var title = e ? e.getAttribute("tiddler") : null;
return title ? store.getTiddler(title) : null;
};
config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);
if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the "in" clause
var tiddlyWikiPath = undefined;
if ((i < params.length) && params[i] == "in") {
i++;
if (i >= params.length) {
this.handleError(place, "TiddlyWiki path expected behind 'in'.");
return;
}
tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the where clause
var whereClause ="true";
if ((i < params.length) && params[i] == "where") {
i++;
whereClause = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the sort stuff
var sortClause = null;
var sortAscending = true;
if ((i < params.length) && params[i] == "sortBy") {
i++;
if (i >= params.length) {
this.handleError(place, "sortClause missing behind 'sortBy'.");
return;
}
sortClause = this.paramEncode(params[i]);
i++;
if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
sortAscending = params[i] == "ascending";
i++;
}
}
// Parse the script
var scriptText = null;
if ((i < params.length) && params[i] == "script") {
i++;
scriptText = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the action.
// When we are already at the end use the default action
var actionName = "addToList";
if (i < params.length) {
if (!config.macros.forEachTiddler.actions[params[i]]) {
this.handleError(place, "Unknown action '"+params[i]+"'.");
return;
} else {
actionName = params[i];
i++;
}
}
// Get the action parameter
// (the parsing is done inside the individual action implementation.)
var actionParameter = params.slice(i);
// --- Processing ------------------------------------------
try {
this.performMacro({
place: place,
inTiddler: tiddler,
whereClause: whereClause,
sortClause: sortClause,
sortAscending: sortAscending,
actionName: actionName,
actionParameter: actionParameter,
scriptText: scriptText,
tiddlyWikiPath: tiddlyWikiPath});
} catch (e) {
this.handleError(place, e);
}
};
// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {
var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);
var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
context["tiddlyWiki"] = tiddlyWiki;
// Get the tiddlers, as defined by the whereClause
var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
context["tiddlers"] = tiddlers;
// Sort the tiddlers, when sorting is required.
if (parameter.sortClause) {
this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
}
return {tiddlers: tiddlers, context: context};
};
// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
return this.getTiddlersAndContext(parameter).tiddlers;
};
// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
var tiddlersAndContext = this.getTiddlersAndContext(parameter);
// Perform the action
var actionName = parameter.actionName ? parameter.actionName : "addToList";
var action = config.macros.forEachTiddler.actions[actionName];
if (!action) {
this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
return;
}
var actionHandler = action.handler;
actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};
// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------
// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
return;
}
// Perform the action.
var list = document.createElement("ul");
place.appendChild(list);
for (var i = 0; i < tiddlers.length; i++) {
var tiddler = tiddlers[i];
var listItem = document.createElement("li");
list.appendChild(listItem);
createTiddlyLink(listItem, tiddler.title, true);
}
};
abego.parseNamedParameter = function(name, parameter, i) {
var beginExpression = null;
if ((i < parameter.length) && parameter[i] == name) {
i++;
if (i >= parameter.length) {
throw "Missing text behind '%0'".format([name]);
}
return config.macros.forEachTiddler.paramEncode(parameter[i]);
}
return null;
}
// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
if (p >= parameter.length) {
this.handleError(place, "Missing expression behind 'write'.");
return;
}
var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
// Parse the "begin" option
var beginExpression = abego.parseNamedParameter("begin", parameter, p);
if (beginExpression !== null)
p += 2;
var endExpression = abego.parseNamedParameter("end", parameter, p);
if (endExpression !== null)
p += 2;
var noneExpression = abego.parseNamedParameter("none", parameter, p);
if (noneExpression !== null)
p += 2;
// Parse the "toFile" option
var filename = null;
var lineSeparator = undefined;
if ((p < parameter.length) && parameter[p] == "toFile") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
return;
}
filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
p++;
if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
return;
}
lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
}
}
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
return;
}
// Perform the action.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
var count = tiddlers.length;
var text = "";
if (count > 0 && beginExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
for (var i = 0; i < count; i++) {
var tiddler = tiddlers[i];
text += func(tiddler, context, count, i);
}
if (count > 0 && endExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);
if (count == 0 && noneExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
if (filename) {
if (lineSeparator !== undefined) {
lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
text = text.replace(/\n/mg,lineSeparator);
}
saveFile(filename, convertUnicodeToUTF8(text));
} else {
var wrapper = createTiddlyElement(place, "span");
wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
}
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
return {
place : placeParam,
whereClause : whereClauseParam,
sortClause : sortClauseParam,
sortAscending : sortAscendingParam,
script : scriptText,
actionName : actionNameParam,
actionParameter : actionParameterParam,
tiddlyWikiPath : tiddlyWikiPathParam,
inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
};
};
// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
if (!idPrefix) {
idPrefix = "store";
}
var lenPrefix = idPrefix.length;
// Read the content of the given file
var content = loadFile(this.getLocalPath(path));
if(content === null) {
throw "TiddlyWiki '"+path+"' not found.";
}
var tiddlyWiki = new TiddlyWiki();
// Starting with TW 2.2 there is a helper function to import the tiddlers
if (tiddlyWiki.importTiddlyWiki) {
if (!tiddlyWiki.importTiddlyWiki(content))
throw "File '"+path+"' is not a TiddlyWiki.";
tiddlyWiki.dirty = false;
return tiddlyWiki;
}
// The legacy code, for TW < 2.2
// Locate the storeArea div's
var posOpeningDiv = content.indexOf(startSaveArea);
var posClosingDiv = content.lastIndexOf(endSaveArea);
if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
throw "File '"+path+"' is not a TiddlyWiki.";
}
var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
// Create a "div" element that contains the storage text
var myStorageDiv = document.createElement("div");
myStorageDiv.innerHTML = storageText;
myStorageDiv.normalize();
// Create all tiddlers in a new TiddlyWiki
// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
var store = myStorageDiv.childNodes;
for(var t = 0; t < store.length; t++) {
var e = store[t];
var title = null;
if(e.getAttribute)
title = e.getAttribute("tiddler");
if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
title = e.id.substr(lenPrefix);
if(title && title !== "") {
var tiddler = tiddlyWiki.createTiddler(title);
tiddler.loadFromDiv(e,title);
}
}
tiddlyWiki.dirty = false;
return tiddlyWiki;
};
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
var script = context["script"];
var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
var fullText = (script ? script+";" : "")+functionText+";theFunction;";
return eval(fullText);
};
// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
var result = [];
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
tiddlyWiki.forEachTiddler(function(title,tiddler) {
if (func(tiddler, context, undefined, undefined)) {
result.push(tiddler);
}
});
return result;
};
// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
var message = "Extra parameter behind '"+actionName+"':";
for (var i = firstUnusedIndex; i < parameter.length; i++) {
message += " "+parameter[i];
}
this.handleError(place, message);
};
// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? -1
: +1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? +1
: -1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
// To avoid evaluating the sortClause whenever two items are compared
// we pre-calculate the sortValue for every item in the array and store it in a
// temporary property ("forEachTiddlerSortValue") of the tiddlers.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
var count = tiddlers.length;
var i;
for (i = 0; i < count; i++) {
var tiddler = tiddlers[i];
tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
}
// Do the sorting
tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);
// Delete the temporary property that holds the sortValue.
for (i = 0; i < tiddlers.length; i++) {
delete tiddlers[i].forEachTiddlerSortValue;
}
};
// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
displayMessage(message);
};
// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
var message ="<<"+macroName;
for (var i = 0; i < params.length; i++) {
message += " "+params[i];
}
message += ">>";
displayMessage(message);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};
// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
if (place) {
this.createErrorElement(place, exception);
} else {
throw exception;
}
};
// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
var reGTGT = new RegExp("\\$\\)\\)","mg");
var reGT = new RegExp("\\$\\)","mg");
return s.replace(reGTGT, ">>").replace(reGT, ">");
};
// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// Remove any location part of the URL
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert to a native file format assuming
// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
"forEachTiddler");
//============================================================================
// End of forEachTiddler Macro
//============================================================================
//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
var n = prefix.length;
return (this.length >= n) && (this.slice(0, n) == prefix);
};
//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
var n = suffix.length;
return (this.length >= n) && (this.right(n) == suffix);
};
//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
return this.indexOf(substring) >= 0;
};
//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
return (this.indexOf(item) >= 0);
};
//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
for(var i = 0; i < items.length; i++) {
if (this.contains(items[i])) {
return true;
}
}
return false;
};
//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
for(var i = 0; i < items.length; i++) {
if (!this.contains(items[i])) {
return false;
}
}
return true;
};
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
displayMessage, endSaveArea, hasClass, loadFile, saveFile,
startSaveArea, store, wikify */
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
/***
|Name|FullScreenPlugin|
|Created by|SaqImtiaz|
|Location|http://lewcid.googlepages.com/lewcid.html#FullScreenPlugin|
|Version|1.1|
|Requires|~TW2.x|
!Description:
Toggle between viewing tiddlers fullscreen and normally. Very handy for when you need more viewing space.
!Demo:
Click the ↕ button in the toolbar for this tiddler. Click it again to turn off fullscreen.
!Installation:
Copy the contents of this tiddler to your TW, tag with systemConfig, save and reload your TW.
Edit the ViewTemplate to add the fullscreen command to the toolbar.
!History:
*25-07-06: ver 1.1
*20-07-06: ver 1.0
!Code
***/
//{{{
var lewcidFullScreen = false;
config.commands.fullscreen =
{
text:" ↕ ",
tooltip:"Fullscreen mode"
};
config.commands.fullscreen.handler = function (event,src,title)
{
if (lewcidFullScreen == false)
{
lewcidFullScreen = true;
setStylesheet('#sidebar, .header, #mainMenu{display:none;} #displayArea{margin:0em 0 0 0 !important;}',"lewcidFullScreenStyle");
}
else
{
lewcidFullScreen = false;
setStylesheet(' ',"lewcidFullScreenStyle");
}
}
config.macros.fullscreen={};
config.macros.fullscreen.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var label = params[0]||" ↕ ";
var tooltip = params[1]||"Fullscreen mode";
createTiddlyButton(place,label,tooltip,config.commands.fullscreen.handler);
}
var lewcid_fullscreen_closeTiddler = Story.prototype.closeTiddler;
Story.prototype.closeTiddler =function(title,animate,slowly)
{
lewcid_fullscreen_closeTiddler.apply(this,arguments);
if (story.isEmpty() && lewcidFullScreen == true)
config.commands.fullscreen.handler();
}
Slider.prototype.lewcidStop = Slider.prototype.stop;
Slider.prototype.stop = function()
{
this.lewcidStop();
if (story.isEmpty() && lewcidFullScreen == true)
config.commands.fullscreen.handler();
}
//}}}
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
/***
| Name:|HideWhenPlugin|
| Description:|Allows conditional inclusion/exclusion in templates|
| Version:|1.0.2|
| Date:|19-Sep-2006|
| Source:|http://mptw.tiddlyspot.com/#HideWhenMacro|
| Author:|Simon Baird <simon.baird@gmail.com>|
For use in ViewTemplate and EditTemplate. Eg
{{{<div macro="showWhen tiddler.tags.contains('Task')">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}
***/
//{{{
merge(config.macros,{
hideWhen: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
if (eval(paramString)) {
removeChildren(place);
place.parentNode.removeChild(place);
}
}},
showWhen: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
config.macros.hideWhen.handler(place,macroName,params,wikifier,'!('+paramString+')',tiddler);
}}
});
//}}}
8/08/06 19:51
Question about use of high pass filtering
Do we need it?
Use it to whiten data. Improves t-values.
If it is used should there be no undershoot for HRF?
Examined by Ollinger, J.M., M. Corbetta, and G.L. Shulman, Separating processes within a trial in event-related functional MRI: II. Analysis. ~NeuroImage, 2001. 13(1): p. 218-229 [[PubMed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=11133324&query_hl=1&itool=pubmed_docsum]].
<<<
We infer from this that high-pass filtering the data can degrade sensitivity in cooperative subjects under relatively
noise-free conditions, but can improve performance under noisy conditions.
<<<
[img[http://d.gitelman.googlepages.com/ollinger_neuroimage_2001_fig8.jpg]]
Figure 8: demonstrates the amount of t-value improvement with HPF. The Gamma and 8-pt time course models (~HC-8pt and ~HC-long) truncated the undershoot. The SPM model used the usual hrf with an undershoot.
The final verbatim recommendations were:
<<<
For block designs and widely spaced eventrelated designs, it removes correlations from the data and reduces variance estimates at the cost of a reduced number of degrees of freedom ([[Friston et al. 1995a|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=9343589&query_hl=2&itool=pubmed_docsum]], [[Friston et al.1995b|http://www.fil.ion.ucl.ac.uk/spm/doc/biblio/Year/1995.complete.html#friston95c]], [[Worsley and Friston, 1995|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=9343600&query_hl=2&itool=pubmed_docsum]]). Since most FMRI experiments have hundreds to thousands of degrees of freedom, this reduction does not have a significant effect on sensitivity. Filtering may also be useful for rapidly presented event-related designs. It again removes correlations from the data and reduces variance estimates, although it also removes some signal. In particular, since filtering removes the postpeak undershoot, it is best done in conjunction with a model for the hemodynamic response that does not include that undershoot.
<<<
----
Skudlarski et al [[Pubmed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=10075901&query_hl=5&itool=pubmed_docsum]]
Looked at data processing and reported good results with high-pass filtering with a cut off frequency of about 0.35 of the stimulus switching frequency.
----
See also
Woolrich, M.W., et al., Temporal Autocorrelation in Univariate Linear Modeling of FMRI Data. ~NeuroImage, 2001. 14(6):1370-1386. [[Pubmed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=11707093&query_hl=6&itool=pubmed_docsum]]
----
Lund, T.E., et al., Non-white noise in fMRI: Does modelling have an impact? ~NeuroImage, 2006. 29(1):54-66. [[PubMed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=pubmed&cmd=Retrieve&dopt=AbstractPlus&list_uids=16099175&query_hl=8&itool=pubmed_docsum]]
----
<<top>>
<<toggleSideBar>><<renameButton '>'>>
<<jump j '' top>>
<<fullscreen f>>
<<saveChanges>><<renameButton s 'Save TiddlyWiki'>>
<<newTiddler>><<renameButton n>>
This is an example of how an image is really a curve, and therefore can be represented by cosine functions (in SPM).
Let's start with a 2D brain image. The green rectangle highlights a single line of voxels.
<html>
<img src="http://d.gitelman.googlepages.com/1line_image_slice.jpg" width="300">
</html>
The voxels have been extracted from the image as a single line.
<html>
<img src="http://d.gitelman.googlepages.com/only_1line.jpg" width="400">
</html>
Instead of displaying the voxels as shades of gray in a picture, let's plot the voxel's position on x-axis and the voxel's intensity or gray matter value on the y-axis
<html>
<img src="http://d.gitelman.googlepages.com/2dplot.jpg" width="400">
</html>
...and we have our curve.
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|3.6.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|config.macros.importTiddlers.handler|
|Description|interactive controls for import/export with filtering.|
This plugin lets you selectively combine tiddlers from any two TiddlyWiki documents. An interactive control panel lets you pick a document to import from, and then select which tiddlers to import, with prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles. Automatically add tags to imported tiddlers so they are easy to find later on. Generates a detailed report of import 'history' in ImportedTiddlers.
!!!!!Documentation
>see [[ImportTiddlersPluginInfo]]
!!!!!Configuration
<<<
__password-protected server settings //(optional, if needed)//:__
>username: <<option txtRemoteUsername>> password: <<option txtRemotePassword>>
>{{{usage: <<option txtRemoteUsername>> <<option txtRemotePassword>>}}}
>''note: these settings are also used by [[LoadTiddlersPlugin]] and [[ExternalTiddlersPlugin]]''
<<<
!!!!!Installation Notes
<<<
* As of 6/27/2007, "patch" functions that provide backward-compatibility with TW2.1.x and earlier have been split into a separate [[ImportTiddlersPluginPatch]] tiddler to reduce installation overhead for //this// plugin. You only need to install the additional plugin tiddler when using ImportTiddlersPlugin in documents using TW2.1.x or earlier.
* As of 3/21/2007, the interactive {{{<<importTiddlers>>}}} and non-interactive {{{<<loadTiddlers>>}}} macro definitions and related code have been split into separate [[ImportTiddlersPlugin]] and [[LoadTiddlersPlugin]] to permit selective installation of either the interactive and/or non-interactive macro functions.
* Quick Installation Tip: If you are using an unmodified version of TiddlyWiki (core release version <<version>>), you can get a new, empty TiddlyWiki with the Import Tiddlers plugin pre-installed (''[[download from here|TW+ImportExport.html]]''), and then simply import all your content from your old document into this new, empty document.
<<<
!!!!!Revisions
<<<
2008.01.03 [3.6.0] in loadRemoteFile(), use lower-level doHttp() instead of loadRemoteFile() in order to support username/password access to remote server
2007.10.30 [3.5.6] update [[ImportTiddlers]] shadow tiddler definition to include "inline" link, so the plugin control panel is displayed instead of the standard core interface.
|please see [[ImportTiddlersPluginInfo]] for additional revision details|
2005.07.20 [1.0.0] Initial Release
<<<
!!!!!Code
***/
// // ''MACRO DEFINITION''
//{{{
// Version
version.extensions.importTiddlers = {major: 3, minor: 6, revision: 0, date: new Date(2008,1,3)};
// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;
// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;
// default shadow definition
config.shadowTiddlers.ImportTiddlers="<<importTiddlers inline>>";
merge(config.macros.importTiddlers,{
label: "import tiddlers",
prompt: "Copy tiddlers from another document",
openMsg: "Opening %0",
openErrMsg: "Could not open %0 - error=%1",
readMsg: "Read %0 bytes from %1",
foundMsg: "Found %0 tiddlers in %1",
countMsg: "%0 tiddlers selected for import",
importedMsg: "Imported %0 of %1 tiddlers from %2",
loadText: "please load a document...",
closeText: "close", // text for close button when remote file is loaded
doneText: "done", // text for close button when remote file is not loaded
src: "", // path/filename or URL of document to import (retrieved from SiteUrl tiddler)
proxy: "", // URL for remote proxy script (retrieved from SiteProxy tiddler)
useProxy: false, // use specific proxy script in front of remote URL
inbound: null, // hash-indexed array of tiddlers from other document
newTags: "", // text of tags added to imported tiddlers
addTags: true, // add new tags to imported tiddlers
listsize: 8, // # of lines to show in imported tiddler list
importTags: true, // include tags from remote source document when importing a tiddler
keepTags: true, // retain existing tags when replacing a tiddler
index: 0, // current processing index in import list
sort: "" // sort order for imported tiddler listbox
});
if (config.macros.importTiddlers.coreHandler==undefined)
config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler; // save built-in handler
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
if (config.macros.importTiddlers.coreHandler)
config.macros.importTiddlers.coreHandler.apply(this,arguments);
else
createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
}
else if (params[0]=='link') { // show link to floating panel
var label=params[1]?params[1]:this.label;
var prompt=params[2]?params[2]:this.prompt;
createTiddlyButton(place,label,prompt,onClickImportMenu);
}
else if (params[0]=='inline') {// show panel as INLINE tiddler content
createImportPanel(place);
document.getElementById("importPanel").style.position="static";
document.getElementById("importPanel").style.display="block";
}
else if (config.macros.loadTiddlers)
config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}
//}}}
// // ''INTERFACE DEFINITION''
// // Handle link click to create/show/hide control panel
//{{{
function onClickImportMenu(e)
{
if (!e) var e = window.event;
var parent=resolveTarget(e).parentNode;
var panel = document.getElementById("importPanel");
if (panel==undefined || panel.parentNode!=parent)
panel=createImportPanel(parent);
var isOpen = panel.style.display=="block";
if(config.options.chkAnimate)
anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,"none"));
else
panel.style.display = isOpen ? "none" : "block" ;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
return(false);
}
//}}}
// // Create control panel: HTML, CSS
//{{{
function createImportPanel(place) {
var panel=document.getElementById("importPanel");
if (panel) { panel.parentNode.removeChild(panel); }
setStylesheet(config.macros.importTiddlers.css,"importTiddlers");
panel=createTiddlyElement(place,"span","importPanel",null,null)
panel.innerHTML=config.macros.importTiddlers.html;
refreshImportList();
var siteURL=store.getTiddlerText("SiteUrl"); if (!siteURL) siteURL="";
document.getElementById("importSourceURL").value=siteURL;
config.macros.importTiddlers.src=siteURL;
var siteProxy=store.getTiddlerText("SiteProxy"); if (!siteProxy) siteProxy="SiteProxy";
document.getElementById("importSiteProxy").value=siteProxy;
config.macros.importTiddlers.proxy=siteProxy;
return panel;
}
//}}}
// // CSS
//{{{
config.macros.importTiddlers.css = '\
#importPanel {\
display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;\
background-color: #eee; color:#000; font-size: 8pt; line-height:110%;\
border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;\
padding: 0.5em; margin:0em; -moz-border-radius:1em;\
}\
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }\
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }\
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }\
#importPanel select { width:98%;margin:0px;font-size:8pt;line-height:110%;}\
#importPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}\
#importPanel .box { border:1px solid black; padding:3px; margin-bottom:5px; background:#f8f8f8; -moz-border-radius:5px;}\
#importPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }\
#importPanel .rad { width:auto; }\
#importPanel .chk { width:auto; margin:1px;border:0; }\
#importPanel .btn { width:auto; }\
#importPanel .btn1 { width:98%; }\
#importPanel .btn2 { width:48%; }\
#importPanel .btn3 { width:32%; }\
#importPanel .btn4 { width:24%; }\
#importPanel .btn5 { width:19%; }\
#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }\
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }\
#importCollisionPanel { display:none; margin:0.5em 0em 0em 0em; }\
';
//}}}
// // HTML
//{{{
config.macros.importTiddlers.html = '\
<!-- source and report -->\
<table><tr><td align=left>\
import from\
<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED\
onClick="document.getElementById(\'importLocalPanel\').style.display=this.checked?\'block\':\'none\';\
document.getElementById(\'importHTTPPanel\').style.display=!this.checked?\'block\':\'none\'"> local file\
<input type="radio" class="rad" name="importFrom" id="importFromWeb" value="http"\
onClick="document.getElementById(\'importLocalPanel\').style.display=!this.checked?\'block\':\'none\';\
document.getElementById(\'importHTTPPanel\').style.display=this.checked?\'block\':\'none\'"> web server\
</td><td align=right>\
<input type=checkbox class="chk" id="chkImportReport" checked\
onClick="config.options[\'chkImportReport\']=this.checked;"> create a report\
</td></tr></table>\
<!-- import from local file -->\
<div id="importLocalPanel" style="display:block;margin-bottom:5px;margin-top:5px;padding-top:3px;border-top:1px solid #999">\
local document path/filename:<br>\
<input type="file" id="fileImportSource" size=57 style="width:100%"\
onKeyUp="config.macros.importTiddlers.src=this.value"\
onChange="config.macros.importTiddlers.src=this.value;">\
</div><!--panel-->\
\
<!-- import from http server -->\
<div id="importHTTPPanel" style="display:none;margin-bottom:5px;margin-top:5px;padding-top:3px;border-top:1px solid #999">\
<table><tr><td align=left>\
remote document URL:<br>\
</td><td align=right>\
<input type="checkbox" class="chk" id="importUseProxy"\
onClick="config.macros.importTiddlers.useProxy=this.checked;\
document.getElementById(\'importSiteProxy\').style.display=this.checked?\'block\':\'none\'"> use a proxy script\
</td></tr></table>\
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"\
onKeyUp="config.macros.importTiddlers.proxy=this.value"\
onChange="config.macros.importTiddlers.proxy=this.value;">\
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"\
onKeyUp="config.macros.importTiddlers.src=this.value"\
onChange="config.macros.importTiddlers.src=this.value;">\
</div><!--panel-->\
\
<table><tr><td align=left>\
select:\
<a href="JavaScript:;" id="importSelectAll"\
onclick="onClickImportButton(this)" title="select all tiddlers">\
all </a>\
<a href="JavaScript:;" id="importSelectNew"\
onclick="onClickImportButton(this)" title="select tiddlers not already in destination document">\
added </a> \
<a href="JavaScript:;" id="importSelectChanges"\
onclick="onClickImportButton(this)" title="select tiddlers that have been updated in source document">\
changes </a> \
<a href="JavaScript:;" id="importSelectDifferences"\
onclick="onClickImportButton(this)" title="select tiddlers that have been added or are different from existing tiddlers">\
differences </a> \
<a href="JavaScript:;" id="importToggleFilter"\
onclick="onClickImportButton(this)" title="show/hide selection filter">\
filter </a> \
</td><td align=right>\
<a href="JavaScript:;" id="importListSmaller"\
onclick="onClickImportButton(this)" title="reduce list size">\
– </a>\
<a href="JavaScript:;" id="importListLarger"\
onclick="onClickImportButton(this)" title="increase list size">\
+ </a>\
<a href="JavaScript:;" id="importListMaximize"\
onclick="onClickImportButton(this)" title="maximize/restore list size">\
= </a>\
</td></tr></table>\
<select id="importList" size=8 multiple\
onchange="setTimeout(\'refreshImportList(\'+this.selectedIndex+\')\',1)">\
<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->\
</select>\
<input type=checkbox class="chk" id="chkAddTags" checked\
onClick="config.macros.importTiddlers.addTags=this.checked;">add new tags \
<input type=checkbox class="chk" id="chkImportTags" checked\
onClick="config.macros.importTiddlers.importTags=this.checked;">import source tags \
<input type=checkbox class="chk" id="chkKeepTags" checked\
onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing tags<br>\
<input type=text id="txtNewTags" size=15 onKeyUp="config.macros.importTiddlers.newTags=this.value" autocomplete=off>\
<div align=center>\
<input type=button id="importLoad" class="importButton" style="width:32%" value="load"\
title="load listbox with tiddlers from source document"\
onclick="onClickImportButton(this)">\
<input type=button id="importStart" class="importButton" style="width:32%" value="import"\
title="add selected source tiddlers to the current document"\
onclick="onClickImportButton(this)">\
<input type=button id="importClose" class="importButton" style="width:32%" value="close"\
title="clear listbox or hide control panel"\
onclick="onClickImportButton(this)">\
</div>\
<div id="importCollisionPanel">\
tiddler already exists:\
<input type=text id="importNewTitle" size=15 autocomplete=off">\
<div align=center>\
<input type=button id="importSkip" class="importButton" style="width:23%" value="skip"\
title="do not import this tiddler"\
onclick="onClickImportButton(this)">\
<input type=button id="importRename" class="importButton" style="width:23%" value="rename"\
title="rename the incoming tiddler"\
onclick="onClickImportButton(this)">\
<input type=button id="importMerge" class="importButton" style="width:23%" value="merge"\
title="append the incoming tiddler to the existing tiddler"\
onclick="onClickImportButton(this)">\
<input type=button id="importReplace" class="importButton" style="width:23%" value="replace"\
title="discard the existing tiddler"\
onclick="onClickImportButton(this)">\
</div>\
</div>\
';
//}}}
// // Control interactions
//{{{
function onClickImportButton(which)
{
// DEBUG alert(which.id);
var theList = document.getElementById('importList');
if (!theList) return;
var thePanel = document.getElementById('importPanel');
var theCollisionPanel = document.getElementById('importCollisionPanel');
var theNewTitle = document.getElementById('importNewTitle');
var count=0;
switch (which.id)
{
case 'fileImportSource':
case 'importLoad': // load import source into hidden frame
importReport(); // if an import was in progress, generate a report
config.macros.importTiddlers.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
if (config.macros.importTiddlers.src=="") break;
// Load document, read it's DOM and fill the list
config.macros.importTiddlers.loadRemoteFile(config.macros.importTiddlers.src,
function(success,params,txt,src,xhr) {
var src=src.replace(/%20/g," ");
if (!success) { displayMessage(config.macros.importTiddlers.openErrMsg.format([src,xhr.status])); return; }
var tiddlers = config.macros.importTiddlers.readTiddlersFromHTML(txt);
var count=tiddlers?tiddlers.length:0;
var querypos=src.lastIndexOf("?"); if (querypos!=-1) src=src.substr(0,querypos);
displayMessage(config.macros.importTiddlers.foundMsg.format([count,src]));
config.macros.importTiddlers.inbound=tiddlers;
window.refreshImportList(0);
});
break;
case 'importSelectAll': // select all tiddler list items (i.e., not headings)
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < theList.options.length; t++) {
if (theList.options[t].value=="") continue;
theList.options[t].selected=true;
count++;
}
clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
break;
case 'importSelectNew': // select tiddlers not in current document
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < theList.options.length; t++) {
theList.options[t].selected=false;
if (theList.options[t].value=="") continue;
theList.options[t].selected=!store.tiddlerExists(theList.options[t].value);
count+=theList.options[t].selected?1:0;
}
clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
break;
case 'importSelectChanges': // select tiddlers that are updated from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < theList.options.length; t++) {
theList.options[t].selected=false;
if (theList.options[t].value==""||!store.tiddlerExists(theList.options[t].value)) continue;
for (var i=0; i<config.macros.importTiddlers.inbound.length; i++) // find matching inbound tiddler
{ var inbound=config.macros.importTiddlers.inbound[i]; if (inbound.title==theList.options[t].value) break; }
theList.options[t].selected=(inbound.modified-store.getTiddler(theList.options[t].value).modified>0); // updated tiddler
count+=theList.options[t].selected?1:0;
}
clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
break;
case 'importSelectDifferences': // select tiddlers that are new or different from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < theList.options.length; t++) {
theList.options[t].selected=false;
if (theList.options[t].value=="") continue;
if (!store.tiddlerExists(theList.options[t].value)) { theList.options[t].selected=true; count++; continue; }
for (var i=0; i<config.macros.importTiddlers.inbound.length; i++) // find matching inbound tiddler
{ var inbound=config.macros.importTiddlers.inbound[i]; if (inbound.title==theList.options[t].value) break; }
theList.options[t].selected=(inbound.modified-store.getTiddler(theList.options[t].value).modified!=0); // changed tiddler
count+=theList.options[t].selected?1:0;
}
clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
break;
case 'importToggleFilter': // show/hide filter
case 'importFilter': // apply filter
alert("coming soon!");
break;
case 'importStart': // initiate the import processing
importReport(); // if an import was in progress, generate a report
config.macros.importTiddlers.index=0;
config.macros.importTiddlers.index=importTiddlers(0);
importStopped();
break;
case 'importClose': // unload imported tiddlers or hide the import control panel
// if imported tiddlers not loaded, close the import control panel
if (!config.macros.importTiddlers.inbound) { thePanel.style.display='none'; break; }
importReport(); // if an import was in progress, generate a report
config.macros.importTiddlers.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
break;
case 'importSkip': // don't import the tiddler
var theItem = theList.options[config.macros.importTiddlers.index];
for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
var theImported = config.macros.importTiddlers.inbound[j];
theImported.status='skipped after asking'; // mark item as skipped
theCollisionPanel.style.display='none';
config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index+1); // resume with NEXT item
importStopped();
break;
case 'importRename': // change name of imported tiddler
var theItem = theList.options[config.macros.importTiddlers.index];
for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
var theImported = config.macros.importTiddlers.inbound[j];
theImported.status = 'renamed from '+theImported.title; // mark item as renamed
theImported.set(theNewTitle.value,null,null,null,null); // change the tiddler title
theItem.value = theNewTitle.value; // change the listbox item text
theItem.text = theNewTitle.value; // change the listbox item text
theCollisionPanel.style.display='none';
config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index); // resume with THIS item
importStopped();
break;
case 'importMerge': // join existing and imported tiddler content
var theItem = theList.options[config.macros.importTiddlers.index];
for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
var theImported = config.macros.importTiddlers.inbound[j];
var theExisting = store.getTiddler(theItem.value);
var theText = theExisting.text+'\n----\n^^merged from: ';
theText +='[['+config.macros.importTiddlers.src+'#'+theItem.value+'|'+config.macros.importTiddlers.src+'#'+theItem.value+']]^^\n';
theText +='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
var theDate = new Date();
var theTags = theExisting.getTags()+' '+theImported.getTags();
theImported.set(null,theText,null,theDate,theTags);
theImported.status = 'merged with '+theExisting.title; // mark item as merged
theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index); // resume with this item
importStopped();
break;
case 'importReplace': // substitute imported tiddler for existing tiddler
var theItem = theList.options[config.macros.importTiddlers.index];
for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
if (config.macros.importTiddlers.inbound[j].title==theItem.value) break;
var theImported = config.macros.importTiddlers.inbound[j];
var theExisting = store.getTiddler(theItem.value);
theImported.status = 'replaces '+theExisting.title; // mark item for replace
theImported.status += ' - '+theExisting.modified.formatString("MM/DD/YYYY 0hh:0mm:0ss");
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
config.macros.importTiddlers.index=importTiddlers(config.macros.importTiddlers.index); // resume with THIS item
importStopped();
break;
case 'importListSmaller': // decrease current listbox size, minimum=5
if (theList.options.length==1) break;
theList.size-=(theList.size>5)?1:0;
config.macros.importTiddlers.listsize=theList.size;
break;
case 'importListLarger': // increase current listbox size, maximum=number of items in list
if (theList.options.length==1) break;
theList.size+=(theList.size<theList.options.length)?1:0;
config.macros.importTiddlers.listsize=theList.size;
break;
case 'importListMaximize': // toggle listbox size between current and maximum
if (theList.options.length==1) break;
theList.size=(theList.size==theList.options.length)?config.macros.importTiddlers.listsize:theList.options.length;
break;
}
}
//}}}
// // refresh listbox
//{{{
function refreshImportList(selectedIndex)
{
var theList = document.getElementById("importList");
if (!theList) return;
// if nothing to show, reset list content and size
if (!config.macros.importTiddlers.inbound)
{
while (theList.length > 0) { theList.options[0] = null; }
theList.options[0]=new Option(config.macros.importTiddlers.loadText,"",false,false);
theList.size=config.macros.importTiddlers.listsize;
document.getElementById('importLoad').disabled=false;
document.getElementById('fileImportSource').disabled=false;
document.getElementById('importFromFile').disabled=false;
document.getElementById('importFromWeb').disabled=false;
document.getElementById('importClose').value=config.macros.importTiddlers.closeText;
return;
}
// get the sort order
if (!selectedIndex) selectedIndex=0;
if (selectedIndex==0) config.macros.importTiddlers.sort='title'; // heading
if (selectedIndex==1) config.macros.importTiddlers.sort='title';
if (selectedIndex==2) config.macros.importTiddlers.sort='modified';
if (selectedIndex==3) config.macros.importTiddlers.sort='tags';
if (selectedIndex>3) {
// display selected tiddler count
for (var t=0,count=0; t < theList.options.length; t++) {
if (!theList.options[t].selected) continue;
if (theList.options[t].value!="")
count+=1;
else { // if heading is selected, deselect it, and then select and count all in section
theList.options[t].selected=false;
for ( t++; t<theList.options.length && theList.options[t].value!=""; t++) {
theList.options[t].selected=true;
count++;
}
}
}
clearMessage(); displayMessage(config.macros.importTiddlers.countMsg.format([count]));
return; // no refresh needed
}
// there are inbound tiddlers loaded... disable inapplicable controls...
document.getElementById('importLoad').disabled=true;
document.getElementById('fileImportSource').disabled=true;
document.getElementById('importFromFile').disabled=true;
document.getElementById('importFromWeb').disabled=true;
document.getElementById('importClose').value=config.macros.importTiddlers.doneText;
// get the alphasorted list of tiddlers (optionally, filter out unchanged tiddlers)
var tiddlers=config.macros.importTiddlers.inbound;
tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
// clear current list contents
while (theList.length > 0) { theList.options[0] = null; }
// add heading and control items to list
var i=0;
var indent=String.fromCharCode(160)+String.fromCharCode(160);
theList.options[i++]=new Option(tiddlers.length+' tiddler'+((tiddlers.length!=1)?'s are':' is')+' in the document',"",false,false);
theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="title" )?">":indent)+' [by title]',"",false,false);
theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="modified")?">":indent)+' [by date]',"",false,false);
theList.options[i++]=new Option(((config.macros.importTiddlers.sort=="tags")?">":indent)+' [by tags]',"",false,false);
// output the tiddler list
switch(config.macros.importTiddlers.sort)
{
case "title":
for(var t = 0; t < tiddlers.length; t++)
theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
break;
case "modified":
// sort descending for newest date first
tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
var lastSection = "";
for(var t = 0; t < tiddlers.length; t++) {
var tiddler = tiddlers[t];
var theSection = tiddler.modified.toLocaleDateString();
if (theSection != lastSection) {
theList.options[i++] = new Option(theSection,"",false,false);
lastSection = theSection;
}
theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
}
break;
case "tags":
var theTitles = {}; // all tiddler titles, hash indexed by tag value
var theTags = new Array();
for(var t=0; t<tiddlers.length; t++) {
var title=tiddlers[t].title;
var tags=tiddlers[t].tags;
if (!tags || !tags.length) {
if (theTitles["untagged"]==undefined) { theTags.push("untagged"); theTitles["untagged"]=new Array(); }
theTitles["untagged"].push(title);
}
else for(var s=0; s<tags.length; s++) {
if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
theTitles[tags[s]].push(title);
}
}
theTags.sort();
for(var tagindex=0; tagindex<theTags.length; tagindex++) {
var theTag=theTags[tagindex];
theList.options[i++]=new Option(theTag,"",false,false);
for(var t=0; t<theTitles[theTag].length; t++)
theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
}
break;
}
theList.selectedIndex=selectedIndex; // select current control item
if (theList.size<config.macros.importTiddlers.listsize) theList.size=config.macros.importTiddlers.listsize;
if (theList.size>theList.options.length) theList.size=theList.options.length;
}
//}}}
// // re-entrant processing for handling import with interactive collision prompting
//{{{
function importTiddlers(startIndex)
{
if (!config.macros.importTiddlers.inbound) return -1;
var theList = document.getElementById('importList');
if (!theList) return;
var t;
// if starting new import, reset import status flags
if (startIndex==0)
for (var t=0;t<config.macros.importTiddlers.inbound.length;t++)
config.macros.importTiddlers.inbound[t].status="";
for (var i=startIndex; i<theList.options.length; i++)
{
// if list item is not selected or is a heading (i.e., has no value), skip it
if ((!theList.options[i].selected) || ((t=theList.options[i].value)==""))
continue;
for (var j=0;j<config.macros.importTiddlers.inbound.length;j++)
if (config.macros.importTiddlers.inbound[j].title==t) break;
var inbound = config.macros.importTiddlers.inbound[j];
var theExisting = store.getTiddler(inbound.title);
// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
if (inbound.status=="added")
continue;
// don't import the "ImportedTiddlers" history from the other document...
if (inbound.title=='ImportedTiddlers')
continue;
// if tiddler exists and import not marked for replace or merge, stop importing
if (theExisting && (inbound.status.substr(0,7)!="replace") && (inbound.status.substr(0,5)!="merge"))
return i;
// assemble tags (remote + existing + added)
var newTags = "";
if (config.macros.importTiddlers.importTags)
newTags+=inbound.getTags() // import remote tags
if (config.macros.importTiddlers.keepTags && theExisting)
newTags+=" "+theExisting.getTags(); // keep existing tags
if (config.macros.importTiddlers.addTags && config.macros.importTiddlers.newTags.trim().length)
newTags+=" "+config.macros.importTiddlers.newTags; // add new tags
inbound.set(null,null,null,null,newTags.trim());
// set the status to 'added' (if not already set by the 'ask the user' UI)
inbound.status=(inbound.status=="")?'added':inbound.status;
// do the import!
store.suspendNotifications();
store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
store.resumeNotifications();
}
return(-1); // signals that we really finished the entire list
}
//}}}
//{{{
function importStopped()
{
var theList = document.getElementById('importList');
var theNewTitle = document.getElementById('importNewTitle');
if (!theList) return;
if (config.macros.importTiddlers.index==-1)
importReport(); // import finished... generate the report
else
{
// import collision... show the collision panel and set the title edit field
document.getElementById('importCollisionPanel').style.display='block';
theNewTitle.value=theList.options[config.macros.importTiddlers.index].value;
}
}
//}}}
// // ''REPORT GENERATOR''
//{{{
function importReport(quiet)
{
if (!config.macros.importTiddlers.inbound) return;
// DEBUG alert('importReport: start');
// if import was not completed, the collision panel will still be open... close it now.
var panel=document.getElementById('importCollisionPanel'); if (panel) panel.style.display='none';
// get the alphasorted list of tiddlers
var tiddlers = config.macros.importTiddlers.inbound;
// gather the statistics
var count=0;
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].status && tiddlers[t].status.trim().length && tiddlers[t].status.substr(0,7)!="skipped") count++;
// generate a report
if (count && config.options.chkImportReport) {
// get/create the report tiddler
var theReport = store.getTiddler('ImportedTiddlers');
if (!theReport) { theReport= new Tiddler(); theReport.title = 'ImportedTiddlers'; theReport.text = ""; }
// format the report content
var now = new Date();
var newText = "On "+now.toLocaleString()+", "+config.options.txtUserName
newText +=" imported "+count+" tiddler"+(count==1?"":"s")+" from\n[["+config.macros.importTiddlers.src+"|"+config.macros.importTiddlers.src+"]]:\n";
if (config.macros.importTiddlers.addTags && config.macros.importTiddlers.newTags.trim().length)
newText += "imported tiddlers were tagged with: \""+config.macros.importTiddlers.newTags+"\"\n";
newText += "<<<\n";
for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status) newText += "#[["+tiddlers[t].title+"]] - "+tiddlers[t].status+"\n";
newText += "<<<\n";
// update the ImportedTiddlers content and show the tiddler
theReport.text = newText+((theReport.text!="")?'\n----\n':"")+theReport.text;
theReport.modifier = config.options.txtUserName;
theReport.modified = new Date();
store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
if (!quiet) { story.displayTiddler(null,theReport.title,1,null,null,false); story.refreshTiddler(theReport.title,1,true); }
}
// reset status flags
for (var t=0; t<config.macros.importTiddlers.inbound.length; t++) config.macros.importTiddlers.inbound[t].status="";
// mark document as dirty and let display update as needed
if (count) { store.setDirty(true); store.notifyAll(); }
// always show final message when tiddlers were actually loaded
if (count) displayMessage(config.macros.importTiddlers.importedMsg.format([count,tiddlers.length,config.macros.importTiddlers.src.replace(/%20/g," ")]));
}
//}}}
// // File and XMLHttpRequest I/O
//{{{
config.macros.importTiddlers.fileExists=function(theFile) {
var found=false;
// DEBUG: alert('testing fileExists('+theFile+')...');
if(window.Components) {
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
catch(e) { return false; } // security access denied
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try { file.initWithPath(theFile); }
catch(e) { return false; } // invalid directory
found = file.exists();
}
else { // use ActiveX FSO object for MSIE
var fso = new ActiveXObject("Scripting.FileSystemObject");
found = fso.FileExists(theFile)
}
// DEBUG: alert(theFile+" "+(found?"exists":"not found"));
return found;
}
config.macros.importTiddlers.loadRemoteFile = function(src,callback,quiet) {
if (src==undefined || !src.length) return null; // filename is required
if (!quiet) clearMessage();
if (!quiet) displayMessage(this.openMsg.format([src.replace(/%20/g," ")]));
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if src is relative (i.e., not a URL)
if (!this.fileExists(src)) { // if file cannot be found, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf("/"); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf("\\");
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!="http:") src=getLocalPath(src);
}
}
if (src.substr(0,5)!="http:" && src.substr(0,5)!="file:") { // if not a URL, read from local filesystem
var txt=loadFile(src);
if ((txt==null)||(txt==false)) // file didn't load
{ if (!quiet) displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g," "),"(filesystem error)"])); }
else {
if (!quiet) displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g," ")]));
if (callback) callback(true,quiet,convertUTF8ToUnicode(txt),src,null);
}
}
else {
var name=config.options.txtRemoteUsername; var pass=config.options.txtRemotePassword;
var xhr=doHttp("GET",src,null,null,name,pass,callback,quiet,null)
if (!quiet && !xhr) displayMessage(config.macros.importTiddlers.openErrMsg.format([src,"(XMLHTTPRequest error)"]));
}
}
config.macros.importTiddlers.readTiddlersFromHTML=function(html)
{
var remoteStore=new TiddlyWiki();
remoteStore.importTiddlyWiki(html);
return remoteStore.getTiddlers("title");
}
//}}}
/***
|Name:|InstantTimestampPlugin|
|Description:|A handy way to insert timestamps in your tiddler content|
|Version:|1.0.9 ($Rev: 3646 $)|
|Date:|$Date: 2008-02-27 02:34:38 +1000 (Wed, 27 Feb 2008) $|
|Source:|http://mptw.tiddlyspot.com/#InstantTimestampPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Usage
If you enter {ts} in your tiddler content (without the spaces) it will be replaced with a timestamp when you save the tiddler. Full list of formats:
* {ts} or {t} -> timestamp
* {ds} or {d} -> datestamp
* !ts or !t at start of line -> !!timestamp
* !ds or !d at start of line -> !!datestamp
(I added the extra ! since that's how I like it. Remove it from translations below if required)
!!Notes
* Change the timeFormat and dateFormat below to suit your preference.
* See also http://mptw2.tiddlyspot.com/#AutoCorrectPlugin
* You could invent other translations and add them to the translations array below.
***/
//{{{
config.InstantTimestamp = {
// adjust to suit
timeFormat: 'DD/0MM/YY 0hh:0mm',
dateFormat: 'DD/0MM/YY',
translations: [
[/^!ts?$/img, "'!!{{ts{'+now.formatString(config.InstantTimestamp.timeFormat)+'}}}'"],
[/^!ds?$/img, "'!!{{ds{'+now.formatString(config.InstantTimestamp.dateFormat)+'}}}'"],
// thanks Adapted Cat
[/\{ts?\}(?!\}\})/ig,"'{{ts{'+now.formatString(config.InstantTimestamp.timeFormat)+'}}}'"],
[/\{ds?\}(?!\}\})/ig,"'{{ds{'+now.formatString(config.InstantTimestamp.dateFormat)+'}}}'"]
],
excludeTags: [
"noAutoCorrect",
"noTimestamp",
"html",
"CSS",
"css",
"systemConfig",
"systemConfigDisabled",
"zsystemConfig",
"Plugins",
"Plugin",
"plugins",
"plugin",
"javascript",
"code",
"systemTheme",
"systemPalette"
],
excludeTiddlers: [
"StyleSheet",
"StyleSheetLayout",
"StyleSheetColors",
"StyleSheetPrint"
// more?
]
};
TiddlyWiki.prototype.saveTiddler_mptw_instanttimestamp = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields) {
tags = (typeof(tags) == "string") ? tags.readBracketedList() : tags;
var conf = config.InstantTimestamp;
if ( !tags.containsAny(conf.excludeTags) && !conf.excludeTiddlers.contains(newTitle) ) {
var now = new Date();
var trans = config.InstantTimestamp.translations;
for (var i=0;i<trans.length;i++) {
newBody = newBody.replace(trans[i][0], eval(trans[i][1]));
}
}
return this.saveTiddler_mptw_instanttimestamp(title,newTitle,newBody,modifier,modified,tags,fields);
}
// you can override these in StyleSheet
setStylesheet(".ts,.ds { background-color:#ddd; font-style:italic; }","instantTimestampStyles");
//}}}
/***
|''Name:''|IntelliTaggerPlugin|
|''Version:''|1.0.2 (2007-07-25)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IntelliTaggerPlugin Documentation]]|
|''~SourceCode:''|[[IntelliTaggerPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Version History
* 1.0.2 (2007-07-25):
** Feature: "Return" key may be used to accept first tag suggestion (beside "Alt-1")
** Bugfix: Keyboard shortcuts (Alt+3 etc.) shifted
* 1.0.1 (2007-05-18): Improvement: Speedup when using TiddlyWikis with many tags
* 1.0.0 (2006-04-26): Initial release
***/
// /%
if(!version.extensions.IntelliTaggerPlugin){if(!window.abego){window.abego={};}if(!abego.internal){abego.internal={};}abego.alertAndThrow=function(s){alert(s);throw s;};if(version.major<2){abego.alertAndThrow("Use TiddlyWiki 2.0.8 or better to run the IntelliTagger Plugin.");}version.extensions.IntelliTaggerPlugin={major:1,minor:0,revision:2,date:new Date(2007,6,25),type:"plugin",source:"http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin",documentation:"[[IntelliTaggerPlugin Documentation]]",sourcecode:"[[IntelliTaggerPlugin SourceCode]]",author:"Udo Borkowski (ub [at] abego-software [dot] de)",licence:"[[BSD open source license (abego Software)]]",tiddlywiki:"Version 2.0.8 or better",browser:"Firefox 1.5.0.2 or better"};abego.createEllipsis=function(_2){var e=createTiddlyElement(_2,"span");e.innerHTML="…";};abego.isPopupOpen=function(_4){return _4&&_4.parentNode==document.body;};abego.openAsPopup=function(_5){if(_5.parentNode!=document.body){document.body.appendChild(_5);}};abego.closePopup=function(_6){if(abego.isPopupOpen(_6)){document.body.removeChild(_6);}};abego.getWindowRect=function(){return {left:findScrollX(),top:findScrollY(),height:findWindowHeight(),width:findWindowWidth()};};abego.moveElement=function(_7,_8,_9){_7.style.left=_8+"px";_7.style.top=_9+"px";};abego.centerOnWindow=function(_a){if(_a.style.position!="absolute"){throw "abego.centerOnWindow: element must have absolute position";}var _b=abego.getWindowRect();abego.moveElement(_a,_b.left+(_b.width-_a.offsetWidth)/2,_b.top+(_b.height-_a.offsetHeight)/2);};abego.isDescendantOrSelf=function(_c,e){while(e){if(_c==e){return true;}e=e.parentNode;}return false;};abego.toSet=function(_e){var _f={};for(var i=0;i<_e.length;i++){_f[_e[i]]=true;}return _f;};abego.filterStrings=function(_11,_12,_13){var _14=[];for(var i=0;i<_11.length&&(_13===undefined||_14.length<_13);i++){var s=_11[i];if(s.match(_12)){_14.push(s);}}return _14;};abego.arraysAreEqual=function(a,b){if(!a){return !b;}if(!b){return false;}var n=a.length;if(n!=b.length){return false;}for(var i=0;i<n;i++){if(a[i]!=b[i]){return false;}}return true;};abego.moveBelowAndClip=function(_1b,_1c){if(!_1c){return;}var _1d=findPosX(_1c);var _1e=findPosY(_1c);var _1f=_1c.offsetHeight;var _20=_1d;var _21=_1e+_1f;var _22=findWindowWidth();if(_22<_1b.offsetWidth){_1b.style.width=(_22-100)+"px";}var _23=_1b.offsetWidth;if(_20+_23>_22){_20=_22-_23-30;}if(_20<0){_20=0;}_1b.style.left=_20+"px";_1b.style.top=_21+"px";_1b.style.display="block";};abego.compareStrings=function(a,b){return (a==b)?0:(a<b)?-1:1;};abego.sortIgnoreCase=function(arr){var _27=[];var n=arr.length;for(var i=0;i<n;i++){var s=arr[i];_27.push([s.toString().toLowerCase(),s]);}_27.sort(function(a,b){return (a[0]==b[0])?0:(a[0]<b[0])?-1:1;});for(i=0;i<n;i++){arr[i]=_27[i][1];}};abego.getTiddlerField=function(_2d,_2e,_2f){var _30=document.getElementById(_2d.idPrefix+_2e);var e=null;if(_30!=null){var _32=_30.getElementsByTagName("*");for(var t=0;t<_32.length;t++){var c=_32[t];if(c.tagName.toLowerCase()=="input"||c.tagName.toLowerCase()=="textarea"){if(!e){e=c;}if(c.getAttribute("edit")==_2f){e=c;}}}}return e;};abego.setRange=function(_35,_36,end){if(_35.setSelectionRange){_35.setSelectionRange(_36,end);var max=0+_35.scrollHeight;var len=_35.textLength;var top=max*_36/len,bot=max*end/len;_35.scrollTop=Math.min(top,(bot+top-_35.clientHeight)/2);}else{if(_35.createTextRange!=undefined){var _3b=_35.createTextRange();_3b.collapse();_3b.moveEnd("character",end);_3b.moveStart("character",_36);_3b.select();}else{_35.select();}}};abego.internal.TagManager=function(){var _3c=null;var _3d=function(){if(_3c){return;}_3c={};store.forEachTiddler(function(_3e,_3f){for(var i=0;i<_3f.tags.length;i++){var tag=_3f.tags[i];var _42=_3c[tag];if(!_42){_42=_3c[tag]={count:0,tiddlers:{}};}_42.tiddlers[_3f.title]=true;_42.count+=1;}});};var _43=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(_44,_45,_46,_47,_48,_49){var _4a=this.fetchTiddler(_44);var _4b=_4a?_4a.tags:[];var _4c=(typeof _49=="string")?_49.readBracketedList():_49;_43.apply(this,arguments);if(!abego.arraysAreEqual(_4b,_4c)){abego.internal.getTagManager().reset();}};var _4d=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(_4e){var _4f=this.fetchTiddler(_4e);var _50=_4f&&_4f.tags.length>0;_4d.apply(this,arguments);if(_50){abego.internal.getTagManager().reset();}};this.reset=function(){_3c=null;};this.getTiddlersWithTag=function(tag){_3d();var _52=_3c[tag];return _52?_52.tiddlers:null;};this.getAllTags=function(_53){_3d();var _54=[];for(var i in _3c){_54.push(i);}for(i=0;_53&&i<_53.length;i++){_54.pushUnique(_53[i],true);}abego.sortIgnoreCase(_54);return _54;};this.getTagInfos=function(){_3d();var _56=[];for(var _57 in _3c){_56.push([_57,_3c[_57]]);}return _56;};var _58=function(a,b){var a1=a[1];var b1=b[1];var d=b[1].count-a[1].count;return d!=0?d:abego.compareStrings(a[0].toLowerCase(),b[0].toLowerCase());};this.getSortedTagInfos=function(){_3d();var _5e=this.getTagInfos();_5e.sort(_58);return _5e;};this.getPartnerRankedTags=function(_5f){var _60={};for(var i=0;i<_5f.length;i++){var _62=this.getTiddlersWithTag(_5f[i]);for(var _63 in _62){var _64=store.getTiddler(_63);if(!(_64 instanceof Tiddler)){continue;}for(var j=0;j<_64.tags.length;j++){var tag=_64.tags[j];var c=_60[tag];_60[tag]=c?c+1:1;}}}var _68=abego.toSet(_5f);var _69=[];for(var n in _60){if(!_68[n]){_69.push(n);}}_69.sort(function(a,b){var d=_60[b]-_60[a];return d!=0?d:abego.compareStrings(a.toLowerCase(),b.toLowerCase());});return _69;};};abego.internal.getTagManager=function(){if(!abego.internal.gTagManager){abego.internal.gTagManager=new abego.internal.TagManager();}return abego.internal.gTagManager;};(function(){var _6e=2;var _6f=1;var _70=30;var _71;var _72;var _73;var _74;var _75;var _76;if(!abego.IntelliTagger){abego.IntelliTagger={};}var _77=function(){return _72;};var _78=function(tag){return _75[tag];};var _7a=function(s){var i=s.lastIndexOf(" ");return (i>=0)?s.substr(0,i):"";};var _7d=function(_7e){var s=_7e.value;var len=s.length;return (len>0&&s[len-1]!=" ");};var _81=function(_82){var s=_82.value;var len=s.length;if(len>0&&s[len-1]!=" "){_82.value+=" ";}};var _85=function(tag,_87,_88){if(_7d(_87)){_87.value=_7a(_87.value);}story.setTiddlerTag(_88.title,tag,0);_81(_87);abego.IntelliTagger.assistTagging(_87,_88);};var _89=function(n){if(_76&&_76.length>n){return _76[n];}return (_74&&_74.length>n)?_74[n]:null;};var _8b=function(n,_8d,_8e){var _8f=_89(n);if(_8f){_85(_8f,_8d,_8e);}};var _90=function(_91){var pos=_91.value.lastIndexOf(" ");var _93=(pos>=0)?_91.value.substr(++pos,_91.value.length):_91.value;return new RegExp(_93.escapeRegExp(),"i");};var _94=function(_95,_96){var _97=0;for(var i=0;i<_95.length;i++){if(_96[_95[i]]){_97++;}}return _97;};var _99=function(_9a,_9b,_9c){var _9d=1;var c=_9a[_9b];for(var i=_9b+1;i<_9a.length;i++){if(_9a[i][1].count==c){if(_9a[i][0].match(_9c)){_9d++;}}else{break;}}return _9d;};var _a0=function(_a1,_a2){var _a3=abego.internal.getTagManager().getSortedTagInfos();var _a4=[];var _a5=0;for(var i=0;i<_a3.length;i++){var c=_a3[i][1].count;if(c!=_a5){if(_a2&&(_a4.length+_99(_a3,i,_a1)>_a2)){break;}_a5=c;}if(c==1){break;}var s=_a3[i][0];if(s.match(_a1)){_a4.push(s);}}return _a4;};var _a9=function(_aa,_ab){return abego.filterStrings(abego.internal.getTagManager().getAllTags(_ab),_aa);};var _ac=function(){if(!_71){return;}var _ad=store.getTiddlerText("IntelliTaggerMainTemplate");if(!_ad){_ad="<b>Tiddler IntelliTaggerMainTemplate not found</b>";}_71.innerHTML=_ad;applyHtmlMacros(_71,null);refreshElements(_71,null);};var _ae=function(e){if(!e){var e=window.event;}var tag=this.getAttribute("tag");if(_73){_73.call(this,tag,e);}return false;};var _b2=function(_b3){createTiddlyElement(_b3,"span",null,"tagSeparator"," | ");};var _b4=function(_b5,_b6,_b7,_b8,_b9){if(!_b6){return;}var _ba=_b8?abego.toSet(_b8):{};var n=_b6.length;var c=0;for(var i=0;i<n;i++){var tag=_b6[i];if(_ba[tag]){continue;}if(c>0){_b2(_b5);}if(_b9&&c>=_b9){abego.createEllipsis(_b5);break;}c++;var _bf="";var _c0=_b5;if(_b7<10){_c0=createTiddlyElement(_b5,"span",null,"numberedSuggestion");_b7++;var key=_b7<10?""+(_b7):"0";createTiddlyElement(_c0,"span",null,"suggestionNumber",key+") ");var _c2=_b7==1?"Return or ":"";_bf=" (Shortcut: %1Alt-%0)".format([key,_c2]);}var _c3=config.views.wikified.tag.tooltip.format([tag]);var _c4=(_78(tag)?"Remove tag '%0'%1":"Add tag '%0'%1").format([tag,_bf]);var _c5="%0; Shift-Click: %1".format([_c4,_c3]);var btn=createTiddlyButton(_c0,tag,_c5,_ae,_78(tag)?"currentTag":null);btn.setAttribute("tag",tag);}};var _c7=function(){if(_71){window.scrollTo(0,ensureVisible(_71));}if(_77()){window.scrollTo(0,ensureVisible(_77()));}};var _c8=function(e){if(!e){var e=window.event;}if(!_71){return;}var _cb=resolveTarget(e);if(_cb==_77()){return;}if(abego.isDescendantOrSelf(_71,_cb)){return;}abego.IntelliTagger.close();};addEvent(document,"click",_c8);var _cc=Story.prototype.gatherSaveFields;Story.prototype.gatherSaveFields=function(e,_ce){_cc.apply(this,arguments);var _cf=_ce.tags;if(_cf){_ce.tags=_cf.trim();}};var _d0=function(_d1){story.focusTiddler(_d1,"tags");var _d2=abego.getTiddlerField(story,_d1,"tags");if(_d2){var len=_d2.value.length;abego.setRange(_d2,len,len);window.scrollTo(0,ensureVisible(_d2));}};var _d4=config.macros.edit.handler;config.macros.edit.handler=function(_d5,_d6,_d7,_d8,_d9,_da){_d4.apply(this,arguments);var _db=_d7[0];if((_da instanceof Tiddler)&&_db=="tags"){var _dc=_d5.lastChild;_dc.onfocus=function(e){abego.IntelliTagger.assistTagging(_dc,_da);setTimeout(function(){_d0(_da.title);},100);};_dc.onkeyup=function(e){if(!e){var e=window.event;}if(e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.keyCode>=48&&e.keyCode<=57)){_8b(e.keyCode==48?9:e.keyCode-49,_dc,_da);}else{if(e.ctrlKey&&e.keyCode==32){_8b(0,_dc,_da);}}if(!e.ctrlKey&&(e.keyCode==13||e.keyCode==10)){_8b(0,_dc,_da);}setTimeout(function(){abego.IntelliTagger.assistTagging(_dc,_da);},100);return false;};_81(_dc);}};var _e0=function(e){if(!e){var e=window.event;}var _e3=resolveTarget(e);var _e4=_e3.getAttribute("tiddler");if(_e4){story.displayTiddler(_e3,_e4,"IntelliTaggerEditTagsTemplate",false);_d0(_e4);}return false;};var _e5=config.macros.tags.handler;config.macros.tags.handler=function(_e6,_e7,_e8,_e9,_ea,_eb){_e5.apply(this,arguments);abego.IntelliTagger.createEditTagsButton(_eb,createTiddlyElement(_e6.lastChild,"li"));};var _ec=function(){if(_71&&_72&&!abego.isDescendantOrSelf(document,_72)){abego.IntelliTagger.close();}};setInterval(_ec,100);abego.IntelliTagger.displayTagSuggestions=function(_ed,_ee,_ef,_f0,_f1){_74=_ed;_75=abego.toSet(_ee);_76=_ef;_72=_f0;_73=_f1;if(!_71){_71=createTiddlyElement(document.body,"div",null,"intelliTaggerSuggestions");_71.style.position="absolute";}_ac();abego.openAsPopup(_71);if(_77()){var w=_77().offsetWidth;if(_71.offsetWidth<w){_71.style.width=(w-2*(_6e+_6f))+"px";}abego.moveBelowAndClip(_71,_77());}else{abego.centerOnWindow(_71);}_c7();};abego.IntelliTagger.assistTagging=function(_f3,_f4){var _f5=_90(_f3);var s=_f3.value;if(_7d(_f3)){s=_7a(s);}var _f7=s.readBracketedList();var _f8=_f7.length>0?abego.filterStrings(abego.internal.getTagManager().getPartnerRankedTags(_f7),_f5,_70):_a0(_f5,_70);abego.IntelliTagger.displayTagSuggestions(_a9(_f5,_f7),_f7,_f8,_f3,function(tag,e){if(e.shiftKey){onClickTag.call(this,e);}else{_85(tag,_f3,_f4);}});};abego.IntelliTagger.close=function(){abego.closePopup(_71);_71=null;return false;};abego.IntelliTagger.createEditTagsButton=function(_fb,_fc,_fd,_fe,_ff,id,_101){if(!_fd){_fd="[edit]";}if(!_fe){_fe="Edit the tags";}if(!_ff){_ff="editTags";}var _102=createTiddlyButton(_fc,_fd,_fe,_e0,_ff,id,_101);_102.setAttribute("tiddler",(_fb instanceof Tiddler)?_fb.title:String(_fb));return _102;};abego.IntelliTagger.getSuggestionTagsMaxCount=function(){return 100;};config.macros.intelliTagger={label:"intelliTagger",handler:function(_103,_104,_105,_106,_107,_108){var _109=_107.parseParams("list",null,true);var _10a=_109[0]["action"];for(var i=0;_10a&&i<_10a.length;i++){var _10c=_10a[i];var _10d=config.macros.intelliTagger.subhandlers[_10c];if(!_10d){abego.alertAndThrow("Unsupported action '%0'".format([_10c]));}_10d(_103,_104,_105,_106,_107,_108);}},subhandlers:{showTags:function(_10e,_10f,_110,_111,_112,_113){_b4(_10e,_74,_76?_76.length:0,_76,abego.IntelliTagger.getSuggestionTagsMaxCount());},showFavorites:function(_114,_115,_116,_117,_118,_119){_b4(_114,_76,0);},closeButton:function(_11a,_11b,_11c,_11d,_11e,_11f){var _120=createTiddlyButton(_11a,"close","Close the suggestions",abego.IntelliTagger.close);},version:function(_121){var t="IntelliTagger %0.%1.%2".format([version.extensions.IntelliTaggerPlugin.major,version.extensions.IntelliTaggerPlugin.minor,version.extensions.IntelliTaggerPlugin.revision]);var e=createTiddlyElement(_121,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">"+t+"<font>";},copyright:function(_124){var e=createTiddlyElement(_124,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">© 2006-2007 <b><font color=\"red\">abego</font></b> Software<font>";}}};})();config.shadowTiddlers["IntelliTaggerStyleSheet"]="/***\n"+"!~IntelliTagger Stylesheet\n"+"***/\n"+"/*{{{*/\n"+".intelliTaggerSuggestions {\n"+"\tposition: absolute;\n"+"\twidth: 600px;\n"+"\n"+"\tpadding: 2px;\n"+"\tlist-style: none;\n"+"\tmargin: 0;\n"+"\n"+"\tbackground: #eeeeee;\n"+"\tborder: 1px solid DarkGray;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .currentTag {\n"+"\tfont-weight: bold;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .suggestionNumber {\n"+"\tcolor: #808080;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .numberedSuggestion{\n"+"\twhite-space: nowrap;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter {\n"+"\tmargin-top: 4px;\n"+"\tborder-top-width: thin;\n"+"\tborder-top-style: solid;\n"+"\tborder-top-color: #999999;\n"+"}\n"+".intelliTaggerSuggestions .favorites {\n"+"\tborder-bottom-width: thin;\n"+"\tborder-bottom-style: solid;\n"+"\tborder-bottom-color: #999999;\n"+"\tpadding-bottom: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .normalTags {\n"+"\tpadding-top: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter .button {\n"+"\tfont-size: 10px;\n"+"\n"+"\tpadding-left: 0.3em;\n"+"\tpadding-right: 0.3em;\n"+"}\n"+"\n"+"/*}}}*/\n";config.shadowTiddlers["IntelliTaggerMainTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class=\"favorites\" macro=\"intelliTagger action: showFavorites\"></div>\n"+"<div class=\"normalTags\" macro=\"intelliTagger action: showTags\"></div>\n"+"<!-- The Footer (with the Navigation) ============================================ -->\n"+"<table class=\"intelliTaggerFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n"+" <tr>\n"+"\t<td align=\"left\">\n"+"\t\t<span macro=\"intelliTagger action: closeButton\"></span>\n"+"\t</td>\n"+"\t<td align=\"right\">\n"+"\t\t<span macro=\"intelliTagger action: version\"></span>, <span macro=\"intelliTagger action: copyright \"></span>\n"+"\t</td>\n"+" </tr>\n"+"</tbody></table>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["IntelliTaggerEditTagsTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='title' macro='view title'></div>\n"+"<div class='tagged' macro='tags'></div>\n"+"<div class='viewer' macro='view text wikified'></div>\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["BSD open source license (abego Software)"]="See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";config.shadowTiddlers["IntelliTaggerPlugin Documentation"]="[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].";config.shadowTiddlers["IntelliTaggerPlugin SourceCode"]="[[Plugin source code on abego Software website|http://tiddlywiki.abego-software.de/archive/IntelliTaggerPlugin/Plugin-IntelliTagger-src.1.0.2.js]]\n";(function(){var _126=restart;restart=function(){setStylesheet(store.getTiddlerText("IntelliTaggerStyleSheet"),"IntelliTaggerStyleSheet");_126.apply(this,arguments);};})();}
// %/
[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].
In addition to the excellent Plugins found here and throughout the TiddlyWiki world, TiddlyWiki contains some internal Macros that, for the most part, remain undocumented. They can offer lots of interesting possibilities when customizing your TiddlyWiki. The Macro list below was compiled from TiddlyWiki version 1.2.31. Click on a macro's description below to view more detail about each Macro including syntax, description, and examples!
|!Macro|!Description|!Syntax|
|allTags|[[List all Tags in a Tiddler|TipAllTags]]|{{{<}}}{{{<allTags>>}}}|
|closeAll|[[Close all displayed Tiddlers|TipCloseAll]]|{{{<}}}{{{<closeAll>>}}}|
|list all|[[List all Tiddlers in a Tiddler|TipListAll]]|{{{<}}}{{{<list all>>}}}|
|list missing|[[List all Missing Tiddlers in a Tiddler|TipListMissing]]|{{{<}}}{{{<list missing>>}}}|
|list orphans|[[List all orphaned Tiddlers in a Tiddler|TipListOrphans]]|{{{<}}}{{{<list orphans>>}}}|
|newJournal|[[Create new date & Time stamped Tiddler|TipNewJournal]]|{{{<}}}{{{<newJournal>>}}}|
|newTiddler|[[Create new Tiddler|TipNewTiddler]]|{{{<}}}{{{<newTiddler>>}}}|
|permaview|[[URL link for all open Tiddlers|TipPermaView]]|{{{<}}}{{{<permaview>>}}}|
|saveChanges |[[Save all TiddlyWiki changes|TipSaveChanges]]|{{{<}}}{{{<saveChanges>>}}}|
|search|[[Display a Search box|TipSearch]]|{{{<}}}{{{<search>>}}}|
|slider|[[Display a Slider|TipSlider]]|{{{<}}}{{{<slider sliderID sliderTiddler sliderLabel>>}}}|
|tabs|[[Display Tabbed content|TipTabbedContent]]|{{{<}}}{{{<tabs indentifier tabLabel tabName Tiddler>>}}}|
|tag|[[Display a Tag PopUp|TipTagPopUp]]|{{{<}}}{{{<tag tagName>>}}}|
|tiddler|[[Display inline contents of a Tiddler|TipTiddlerContents]]|{{{<}}}{{{<tiddler Tiddler>>}}}|
|timeline|[[Display Timeline in a Tiddler|TipTimeline]]|{{{<}}}{{{<timeline>>}}}|
|today|[[Display Today's Date|TipToday]]|{{{<}}}{{{<today>>}}}|
|version|[[Display TiddlyWiki's version|TipTWVersion]]|{{{<}}}{{{<version>>}}}|
Here is a [[PermaView Link|http://TiddlyWikiTips.com/index.html#%5B%5BTip%20%2312%3A%20TiddlyWiki%20Internal%20Macros%5D%5D%20TipAllTags%20TipCloseAll%20TipNewJournal%20TipNewTiddler%20TipPermaView%20TipSaveChanges%20TipSearch%20TipSlider%20TipTabbedContent%20TipTagPopUp%20TipTiddlerContents%20TipTimeline%20TipToday%20TipTWVersion]] to open all of the Internal Macro Tiddlers!
These figures illustrate the appearance of joint histograms for images of the same modality and different modalities. The images are from the spm5 templates.
<html>
<img src="http://d.gitelman.googlepages.com/hist_orig.jpg" alt="histogram of a T1 image with itself" width="50%">
</html>
Figure 1: Joint histogram of a T1 image with itself. A 45 deg line is seen when images of the same modality are perfectly aligned.
<html>
<img src="http://d.gitelman.googlepages.com/hist_diff.jpg" alt="histogram of a T1 vs T2 image." width="50%">
</html>
Figure 2: Joint histogram of the spm5 template T1 and T2 images. These images should be in excellent alignment. The greater spread than in Figure 1 is due to the images being in different modalities.
/***
|''Name:''|LegacyStrikeThroughPlugin|
|''Description:''|Support for legacy (pre 2.1) strike through formatting|
|''Version:''|1.0.2|
|''Date:''|Jul 21, 2006|
|''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|
|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''License:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.0|
***/
//{{{
// Ensure that the LegacyStrikeThrough Plugin is only installed once.
if(!version.extensions.LegacyStrikeThroughPlugin) {
version.extensions.LegacyStrikeThroughPlugin = {installed:true};
config.formatters.push(
{
name: "legacyStrikeByChar",
match: "==",
termRegExp: /(==)/mg,
element: "strike",
handler: config.formatterHelpers.createElementAndWikify
});
} //# end of "install only once"
//}}}
Directory and file locations are specified by a path name, e.g. /data/studies/Subject01/run1 . Each "/" denotes a different level in the tree structure. Thus / by itself is the root directory, or the top of the tree. /data/studies contains all active data.
You can move between levels in the tree by using cd //pathname// command. Pathname can be the full path (e.g. cd /data/studies/Subject01/run1) or a partial path (e.g. if you are already within the "Subject01" directory and want to move to a subdirectory, "run1", you could type cd run1.
To move up a directory in the path, type ''cd ..''
''ls'' list files and directories in your present working directory
>ls -l show the list in long format
''pwd'' present working directory (i.e. where you are)
''cp'' copy filenames from source to destination
>cp //source_file// //destination_file//
To copy directories use (the {{{"}}}-R{{{"}}} recursively descends into each directory)
>cp -R //source_directory// //destination_directory//
''mv'' moves a file or a directory from the source to the desgination. Can also use mv to rename a file. On linux there is now a {{{"}}}ren{{{"}}} command, which will rename files.
''chmod'' changes the permissions (security) on files. Users will be able to change permissions on the files they own, but generally not on files owned by other users. Root can change permissions on any files
>chmod [-R] //permissions// fileNames
There are various ways to format the file permissions. The permissions are organized by the type of user: //owner = o// //group = g// //world = w// and then by the type of permission.
*read = r or 4
*write = w or 2
*execute = e or 1
The binary representation of the permissions add together.
Let's say a file has security allowing only the owner to read it. The permissions would be 600.
In order to allow other users to read and write a file (writing a file also means the ability to delete it) the command is
>chmod 660 filename
or
>chmod g+rw
using {{{"}}}-R{{{"}}} performs the command recursively (descend into subdirectories and repeat command).
"NB:" A directory must have a minimum of read and execute permissions for you to be able to do a file listing on it.
* Wildcard. It stands for any character.
>cp sourcefile* destination copies all files beginning with "sourcefile" to destination. Note that if destination is not a directory all the files will be copied to a single file.
>cp * destination will copy all files in the present working directory to destination.
''.'' and ''..'' A single dot refers to the current working directory (pwd). Two dots refer to one level higher in the tree than the pwd.
>cp ''..''/file1 ''.'' means to copy file1 (located one level up) to the current working directory.
"~" stands for your home directory.
>cp file1 ~ will copy file1 to /home/animagus.
jump to SPMGuide
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
major: 1, minor: 1, revision: 0,
date: new Date("mar 17, 2007"),
source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};
if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};
bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){
url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
}
return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
// General Options
config.options.chkHttpReadOnly = true;
config.numRssItems = 5; // Number of items in the RSS feed
config.options.txtMaxEditRows = 44;
config.macros.newJournal.label = "new journal";
config.macros.archivedTimeline.dateFormat = "DD-MM-YYYY";
Performs low pass filtering on a timeseries of files. SPM99 functions must be in the Matlab path. SPM99 was the last version of SPM that still had explicit low pass filtering options in spm_filter.m
{{{
function lowpassfilterwithspm99
% LOWPASSFILTERWITHSPM99 low pass filters a timeseries of scans
% SPM99 must be in the Matlab path
% The function assumes a Gaussian filter shape
%
% Matlab R2007a does not seem to run the dll files distributed with
% SPM99. R2006b does seem to work
% Author: Darren Gitelman
% $Id: lowpassfilterwithspm99.m,v 1.0 2007-08-18 18:14:47-05 drg Exp drg $
Finter = spm_figure('Findwin','Interactive');
spm_clf(Finter);
% SELECT THE SCANS TO FILTER AND GET VOLUME INFO
% -------------------------------------------------------------------
P = spm_get(Inf,'.img','Select scans to filter');
Vin = spm_vol(P);
nscans = size(P,1);
xdim = Vin(1).dim(1);
ydim = Vin(1).dim(2);
nslices = Vin(1).dim(3);
% CHECK THAT SCANS HAVE THE SAME ORIENTATION AND DIMENSIONS
% -------------------------------------------------------------------
% if any(any(diff(cat(1,Vin.dim),1,1),1)&[1,1,1,0])
% error('images do not all have the same dimensions'), end
% if any(any(any(diff(cat(3,Vin.mat),1,3),3)))
% error('images do not all have same orientation & voxel size'), end
% FIELD VARIABLES TO SET UP THE FILTER
% FWHM IN SECONDS
% -------------------------------------------------------------------
fwhm = spm_input_ui('Filter width (FWHM) in secs','!1','e',4,[1 1]);
% ACTUAL GAUSSIAN FILTER WIDTH
% -------------------------------------------------------------------
LParam = fwhm/sqrt(8*log(2));
% ASSUME ONE SESSION
% -------------------------------------------------------------------
nsess = 1;
% HIGH PASS (none)
% -------------------------------------------------------------------
cLF = 'none';
HParam = cell(1,nsess);
% GAUSSIAN LOW PASS FILTER
% -------------------------------------------------------------------
cHF = 'Gaussian';
% OTHER VARIABLES
% -------------------------------------------------------------------
row{1} = 1:nscans;
% TR
% -------------------------------------------------------------------
RT = spm_input_ui('TR in secs','!1','e',2,[1 1]);
% ASSEMBLE THE FILTER STRUCTURE
% -------------------------------------------------------------------
K{1} = struct( 'HChoice', cLF,...
'HParam', HParam(1),...
'LChoice', cHF,...
'LParam', LParam,...
'row', row{1},...
'RT', RT);
% GENERATE THE FILTER IN CONVOLUTION FORMAT
% -------------------------------------------------------------------
K{1} = spm_filter('set',K);
% CREATE OUTPUT IMAGES
% -------------------------------------------------------------------
Vout = Vin;
for k=1:nscans
[pth,nm,xt,vr] = fileparts(deblank(Vin(k).fname));
Vout(k).fname = fullfile(pth,['g' nm xt vr]);
desc = [Vout(k).descrip, ' '];
Vout(k).descrip = [desc, sprintf('ts-g-%0.1f',fwhm)];
Vout(k) = spm_create_image(Vout(k));
end
% PROGRESS BAR
% -------------------------------------------------------------------
spm_progress_bar('Init',nslices,'Low pass filter','planes completed')
% READ IN A SLICE ACROSS ALL VOLUMES
% -------------------------------------------------------------------
slices = zeros(xdim,ydim,nscans);
% SLICES
% -------------------------------------------------------------------
for i = 1:Vin(1).dim(3)
% TRANSFORMATION MATRIX TO GET SLICE OF INTEREST
% -------------------------------------------------------------------
B = spm_matrix([0 0 i]);
% READ A SLICE FOR ALL SCANS
% -------------------------------------------------------------------
for m = 1:nscans
slices(:,:,m) = spm_slice_vol(Vin(m),B,Vin(1).dim(1:2),1);
end
% SPM_FILTER ONLY WORKS ON 2D MATRICES
% -------------------------------------------------------------------
Y = reshape(slices,[xdim*ydim nscans]);
% FILTER THAT SLICE
% -------------------------------------------------------------------
Y = spm_filter('apply',K{1},Y);
% RESHAPE BACK TO 3D
% -------------------------------------------------------------------
slices = reshape(Y,[xdim ydim nscans]);
% WRITE OUT SLICE FOR ALL SCANS
% -------------------------------------------------------------------
for m = 1:nscans
Vout(m) = spm_write_plane(Vout(m),slices(:,:,m),i);
end
spm_progress_bar('Set',i);
end
spm_progress_bar('Clear');
fprintf('Low pass filtering completed.\n');
}}}
<html>
<img src="http://d.gitelman.googlepages.com/langpicturemark.jpg" alt="Pretty colored brain picture. ©2006 D.Gitelman", width=110>
</html>
Neurology Brain Imaging Laboratory
----
+++[CALENDARS]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuCalendar")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[NEWS]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuNews")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[CODE]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuCode")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[COMMENTARY]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuCommentary")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[HARDWARE]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuHardware")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[METHODS]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuMethods")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[IMAGING-SOFTWARE]
[[SPM|http://www.fil.ion.ucl.ac.uk/spm]]
[[SPM-HelpList|http://www.jiscmail.ac.uk/lists/spm.html]]
[[FSL|http://www.fmrib.ox.ac.uk/fsl]]
[[FSL-Help|http://www.jiscmail.ac.uk/lists/fsl.html]]
[[AFNI|http://afni.nimh.nih.gov/afni]]
[[MRICRO|http://www.mricro.com]]
[[MRICRO-HelpList|http://www.jiscmail.ac.uk/lists/mricro.html]]
[[Ged Ridgway|http://www.cs.ucl.ac.uk/staff/G.Ridgway/vbm]]
[[Tom Nichols's|http://www.sph.umich.edu/~nichols/links.html]]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuSoftware")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[LOCAL-SPM-NOTES]
<<forEachTiddler
where
'tiddler.tags.contains("MainMenuSPMDoc")'
write
'"[["+tiddler.title+"]]\n"'
>>
===
----
+++[IMAGING-SITES]
[[NU-COGSCI-CBMG|http://cogns.northwestern.edu/cbmg/]]
[[OHBM|http://www.humanbrainmapping.org]]
===
----
+++[GENERAL-RESOURCES]
[[Google Scholar|http://scholar.google.com]]
[[PubMed|http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?DB=pubmed]]
===
----
+++[TIDDLYWIKIS]
[[TiddlySpot Home|http://www.tiddlyspot.com]]
[[TiddlyWiki|http://www.tiddlywiki.com/]]
[[abegoTiddlyWiki|http://tiddlywiki.abego-software.de]]
[[Jack'sTiddlyWiki|http://cawoodm.googlepages.com/jtw.html]]
[[LewcidTiddlyWiki|http://lewcid.googlepages.com/lewcid.html#DropTagsMacroDocumentation]]
[[MonkeyPirate|http://mptw.tiddlyspot.com]]
[[PSoaresTiddlyWiki|http://www.math.ist.utl.pt/~psoares/addons.html]]
[[Tiago'sTiddlyWiki|http://mega.ist.utl.pt/~tngd/twiki/twiki.cgi]]
[[TiddlyMath|http://math.chapman.edu/~jipsen/tiddly/tiddlymath.html]]
[[TiddlyTagWiki|http://www.digitaldimsum.co.uk/]]
[[TiddlyTools|http://www.tiddlytools.com/]]
[[TiddlyWikiExtension|http://www.aurore.eclipse.co.uk/wiki/twextensions.htm]]
[[TiddlyWikiTips|http://tiddlywikitips.com/]]
[[TiddlyWikiTutorial|http://www.blogjones.com/TiddlyWikiTutorial.html]]
[[zRenard-TiddlyWiki|http://www.zrenard.com/tiddlywiki/tiddlywiki.html#HelloTips%20ListTips]]
[[ZenGardenCSS|http://www.csszengarden.com/]]
===
----
+++[DOWNLOAD]
[[TiddlyNBILWiki|http://tiddlyspot.com/?action=download&site=brainimaging]]
===
----
+++[SITE-TOOLS]
[[edit MainMenu|MainMenu]]
AboutTiddlySpot
GettingStarted
ExtendedFormatting
InternalMacros
[[Haloscan|http://www.haloscan.com/]]
[[Control Panel|http://brainimaging.tiddlyspot.com/controlpanel.cgi]]
===
Kia's statements (or your report of Kia's statements) in color.
@@Masking does not require any assumption ~orthogonality. It is used to:
(a) restrict the sampling scope to areas defined by other conditions. (I.e., this is the proper/accepted way of looking for effects of attention within areas showing motivational manipulations, instead of the by-hand way you treated the PPC.)@@
Yes it is used to restrict the sampling scope
When I have done this we have generally restricted contrasts in relation to at least two other contrasts. For example, in a task of semantics, phonology and orthography each with its own baseline (letter) condition we did
Sem-letter masked incl by [ ((sem-letter) - (phon-letter)) & ((sem-letter) - (orth-letter)) ]
Or for eht’s paper
Restricted-search masked incl by [ ((restricted-search - unrestrict-search)) & ((restricted-search) - (working memory)) ]
These contrasts test the null-hypothesis that there is no increased activity for restricted search even in areas where there are differences between restricted-search and unrestricted-search and restricted-search and working memory.
If only a single comparison is used, what is the question?
In your case
full-nontarg masked incl by [ full-nontarg - full-targ ]
This looks for areas showing a positive effect of FT only in areas in which there was a difference between FT and FNT. Note that the difference could be due to higher ft or lower fnt. So if you are only interested in areas showing positive FT this is valid. But is this really the case? It also does not account for whether T and NT differed at baseline, which would still be of concern to reviewers, and should be of concern to us.
As we discussed, the effect of eating may influence responses to both target and non-target stimuli. For example, perhaps it is the case that eating makes the target less appetizing while also increasing the appetitiveness of non-targ, or it could also decrease the appetitiveness of non-targets or leave it unchanged. This situation is very different than in other types of studies.
To wit- responding to phonological differences during one thirty sec period should not influence responses to semantics in another (assuming conditions randomized, etc.). Or making an unrestricted-search should not influence subsequent restricted searches.
In your experiment, eating until one is full could potentially affect ones responses to targets and non-targets. Eating produces a state change. Thus I think it is not valid to look at just part of the result. Unless you want to claim this is an apriori hypothesis. I.e., we only looked at increases in activity to FNT because we originally thought that eating would increase FNT while leaving FT unchanged.
Otherwise you would also have to look at
*FNT masked incl by [ FT - FNT ] and
*FT masked incl by [ FT - FNT ] and
*FT masked incl by [ FNT - FT].
I think this is necessary because the experiment introduced a change in state and not merely a change in stimulus (valid / invalid, words/non-words, etc.).
It would also be more valid if you looked at
* FNT masked incl by [ ( FNT - FT ) & ( HNT - HT ) ].
In that case at least you are examining where is there an effect of FNT but only for voxels that showed differences between NT and T despite the satiety state.
SEE FOR AN EXAMPLE.
Pochon et al., The neural system that bridges reward and cognition in humans: An fmri study. Proc Natl Acad Sci U S A. 2002 April 16; 99(8): 5669–5674.
This article isolates positive and negative activations associated with reward by using masking, but does so in a way that looks at a progression of change – i.e., > 2 levels.
@@(b) ensure that the effects are carried for the right reasons, e.g., showing that in "~A-B", the activation is carried by "A" and not by "-B".@@
You can examine this through the parameter estimates. However, when used in this way it should not confound what is significant by incorrectly inflating the p-values just because it restricts the number of voxels (particularly if you then go and analyze the other half of the comparison.
Here are a set of graphs of the potential parameter estimates if what happened to your data if we assumed that eating did not change any of the full contrasts but that the hungry contrasts were not equivalent at baseline. The conditions are above each graph.
*~FTv = full target valid
*~FNTv = full non-target valid
*~FTn = full target neutral
*~FNTn = full non-target neutral
*~HTv = hungry target valid
*~HNTv = hungry non-target valid
*~HTn = hungry target neutral
*~HNTn = hungry non-target neutral
Notice that only the graph in the upper left looks like yours. Conversely if you can show that ~HTv = ~HNTv AND ~HTn = ~HNTn (by equal I mean no difference or you did not reject the null hypothesis) then your interpretation of your graph is correct.
<html>
<img src="http://d.gitelman.googlepages.com/IFF-graphs1.gif" width=95%
</html>
The PPI machinery in SPM unfortunately does not deal with multisession data correctly. Therefore if you want to run a PPI on a study with multiple sessions you have to do the following:
# The instructions assume that you have multiple sessions or runs for an fMRI study, and that you want to include all or several of the multiple sessions in a PPI analysis.
# Concatenate the onset vectors across sessions
## You will have to refigure the onsets. For example, lets say you have 2 sessions of 10 images each, TR = 2 seconds and you have the following scan onset vectors in seconds sess1 = [0 8 16] and sess2 = [0 8 16]. When you concatenate the vectors the single vector will become allsess = [0 8 16 22 30 38]
These functions were previously part of CBMGtools.
[[getpsth]]
[[mipmaker]]
[[nbilgetselect]]
[[nbilmosaic]]
| !Date | !Topic | !Presenter |
| 04/05/2006 |''Linear Algebra & Matrices'' | Siri |
| 04/26/2006 |''T-tests, Anovas & Regression'' | Mete |
| 05/10/2006 |''GLM'' | Lisa |
| 05/24/2006 |''Covariance, autocorrelation & non-sphericity'' | Appu |
| 06/07/2006 |''What are we measuring? Basis of the BOLD signal in fMRI'' | Bryce |
| 06/21/2006 |''Realign and un-warping'' | James |
| 07/05/2006 |''Co-registration & Spatial Normalization'' | Jane |
| 08/02/2006 |''Study design & efficiency'' | Appu |
| 09/27/2006 |''Overview of [[SPM5|http://www.fil.ion.ucl.ac.uk/spm]] from buttons to codes'' | Emily |
| 09/27/2006 |''Batches and short cuts for [[SPM5|http://www.fil.ion.ucl.ac.uk/spm]]'' | covered above |
| 10/04/2006 |''1st level analysis- Design matrix, contrast & inference'' | Lisa |
| 11/01/2006 |''Temporal basis functions & Correlated Regressors'' | Wen |
| 11/08/2006 |''2nd level analysis- design matrix, contrasts and inferences'' | Tobias |
| 11/29/2006 |''ERP'' | Robert |
| 12/06/2006 | //open - lab meeting // | |
| 12/13/2006 |''Introduction to connectivity- PPI & SEM'' | Emily |
| 12/20/2006 | //open - lab meeting // | |
| 12/27/2006 | //open - lab meeting // | |
| 01/03/2007 | //open - lab meeting // | |
| 01/10/2006 |''Bayes for Beginners'' | James |
| 01/17/2007 | //open - lab meeting // | |
| 01/24/2006 |''Linear Hierarchical models'' | Wen |
| 01/31/2007 | //open - lab meeting // | |
| 02/07/2006 |''DCM for ~FMRI-Theory'' | Siri |
| 02/14/2007 | //open - lab meeting // | |
| 02/21/2007 |''DCM for ~FMRI-Practice'' | Jane |
| 02/28/2007 | //open - lab meeting // | |
| 03/07/2007 |''Eye movement data analysis'' | Robert |
| 03/14/2007 | //open - lab meeting // | |
| 03/21/2007 |''Wavelets'' | Bryce |
| 03/28/2007 | //open - lab meeting // | |
| 04/04/2007 | //open - lab meeting // | |
| 04/11/2007 | //open - lab meeting // | |
| 04/18/2007 | //open - lab meeting // | |
| 04/25/2007 | //open - lab meeting // | |
| 05/02/2007 | //open - lab meeting // | |
| 05/09/2007 | //open - lab meeting // | |
| 05/16/2007 | //open - lab meeting // | |
| 05/23/2007 | //open - lab meeting // | |
| 05/30/2007 | //open - lab meeting // | |
| 06/06/2007 | //open - lab meeting // | |
| 06/13/2007 | //open - lab meeting // | |
| 06/20/2007 | //open - lab meeting // | |
| 06/27/2007 | //open - lab meeting // | |
Open meetings are for lab housekeeping, data presentation, discussion of experimental issues or because something else wasn't scheduled.
If you want to present data at an open meeting please email me: d-gitelman AT northwestern DOT edu
D. Gitelman will be at all meetings to kibbitz.
/***
|Name|NestedSlidersPlugin|
|Source|http://www.TiddlyTools.com/#NestedSlidersPlugin|
|Documentation|http://www.TiddlyTools.com/#NestedSlidersPluginInfo|
|Version|2.3.4|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|show content in nest-able sliding/floating panels, without creating separate tiddlers for each panel's content|
This plugin adds new wiki syntax for embedding 'slider' panels directly into tiddler content.
!!!!!Documentation
>see [[NestedSlidersPluginInfo]]
!!!!!Configuration
<<<
Enable animation for slider panels
<<option chkFloatingSlidersAnimate>> allow sliders to animate when opening/closing
>(note: This setting is in //addition// to the general option for enabling/disabling animation effects:
><<option chkAnimate>> enable animations (entire document)
>For slider animation to occur, you must also allow animation in general.
Debugging messages for 'lazy sliders' deferred rendering:
<<option chkDebugLazySliderDefer>> show debugging alert when deferring slider rendering
<<option chkDebugLazySliderRender>> show debugging alert when deferred slider is actually rendered
<<<
!!!!!Revisions
<<<
2008.01.08 - [*.*.*] plugin size reduction: documentation moved to ...Info tiddler
2007.12.28 - 2.3.4 added hijack for Animator.prototype.startAnimating(). Previously, the plugin code simply set the overflow to "visible" after animation. This code tweak corrects handling of elements that were styled with overflow=hidden/auto/scroll before animation by saving the overflow style and then restoring it after animation has completed.
|please see [[NestedSlidersPluginInfo]] for additional revision details|
2005.11.03 - 1.0.0 initial public release. Thanks to RodneyGomes, GeoffSlocock, and PaulPetterson for suggestions and experiments.
<<<
!!!!!Code
***/
//{{{
version.extensions.nestedSliders = {major: 2, minor: 3, revision: 4, date: new Date(2007,12,28)};
//}}}
//{{{
// options for deferred rendering of sliders that are not initially displayed
if (config.options.chkDebugLazySliderDefer==undefined) config.options.chkDebugLazySliderDefer=false;
if (config.options.chkDebugLazySliderRender==undefined) config.options.chkDebugLazySliderRender=false;
if (config.options.chkFloatingSlidersAnimate==undefined) config.options.chkFloatingSlidersAnimate=false;
// default styles for 'floating' class
setStylesheet(".floatingPanel { position:absolute; z-index:10; padding:0.5em; margin:0em; \
background-color:#eee; color:#000; border:1px solid #000; text-align:left; }","floatingPanelStylesheet");
//}}}
//{{{
config.formatters.push( {
name: "nestedSliders",
match: "\\n?\\+{3}",
terminator: "\\s*\\={3}\\n?",
lookahead: "\\n?\\+{3}(\\+)?(\\([^\\)]*\\))?(\\!*)?(\\^(?:[^\\^\\*\\[\\>]*\\^)?)?(\\*)?(?:\\{\\{([\\w]+[\\s\\w]*)\\{)?(\\[[^\\]]*\\])?(\\[[^\\]]*\\])?(?:\\}{3})?(\\#[^:]*\\:)?(\\>)?(\\.\\.\\.)?\\s*",
handler: function(w)
{
lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
{
// var defopen=lookaheadMatch[1]
// var cookiename=lookaheadMatch[2]
// var header=lookaheadMatch[3]
// var panelwidth=lookaheadMatch[4]
// var transient=lookaheadMatch[5]
// var class=lookaheadMatch[6]
// var label=lookaheadMatch[7]
// var openlabel=lookaheadMatch[8]
// var panelID=lookaheadMatch[9]
// var blockquote=lookaheadMatch[10]
// var deferred=lookaheadMatch[11]
// location for rendering button and panel
var place=w.output;
// default to closed, no cookie, no accesskey, no alternate text/tip
var show="none"; var cookie=""; var key="";
var closedtext=">"; var closedtip="";
var openedtext="<"; var openedtip="";
// extra "+", default to open
if (lookaheadMatch[1]) show="block";
// cookie, use saved open/closed state
if (lookaheadMatch[2]) {
cookie=lookaheadMatch[2].trim().slice(1,-1);
cookie="chkSlider"+cookie;
if (config.options[cookie]==undefined)
{ config.options[cookie] = (show=="block") }
show=config.options[cookie]?"block":"none";
}
// parse label/tooltip/accesskey: [label=X|tooltip]
if (lookaheadMatch[7]) {
var parts=lookaheadMatch[7].trim().slice(1,-1).split("|");
closedtext=parts.shift();
if (closedtext.substr(closedtext.length-2,1)=="=")
{ key=closedtext.substr(closedtext.length-1,1); closedtext=closedtext.slice(0,-2); }
openedtext=closedtext;
if (parts.length) closedtip=openedtip=parts.join("|");
else { closedtip="show "+closedtext; openedtip="hide "+closedtext; }
}
// parse alternate label/tooltip: [label|tooltip]
if (lookaheadMatch[8]) {
var parts=lookaheadMatch[8].trim().slice(1,-1).split("|");
openedtext=parts.shift();
if (parts.length) openedtip=parts.join("|");
else openedtip="hide "+openedtext;
}
var title=show=='block'?openedtext:closedtext;
var tooltip=show=='block'?openedtip:closedtip;
// create the button
if (lookaheadMatch[3]) { // use "Hn" header format instead of button/link
var lvl=(lookaheadMatch[3].length>6)?6:lookaheadMatch[3].length;
var btn = createTiddlyElement(createTiddlyElement(place,"h"+lvl,null,null,null),"a",null,lookaheadMatch[6],title);
btn.onclick=onClickNestedSlider;
btn.setAttribute("href","javascript:;");
btn.setAttribute("title",tooltip);
}
else
var btn = createTiddlyButton(place,title,tooltip,onClickNestedSlider,lookaheadMatch[6]);
btn.innerHTML=title; // enables use of HTML entities in label
// set extra button attributes
btn.setAttribute("closedtext",closedtext);
btn.setAttribute("closedtip",closedtip);
btn.setAttribute("openedtext",openedtext);
btn.setAttribute("openedtip",openedtip);
btn.sliderCookie = cookie; // save the cookiename (if any) in the button object
btn.defOpen=lookaheadMatch[1]!=null; // save default open/closed state (boolean)
btn.keyparam=key; // save the access key letter ("" if none)
if (key.length) {
btn.setAttribute("accessKey",key); // init access key
btn.onfocus=function(){this.setAttribute("accessKey",this.keyparam);}; // **reclaim** access key on focus
}
btn.onmouseover=function(event) // mouseover on button aligns floater position with button
{ if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this,this.sliderPanel); }
// create slider panel
var panelClass=lookaheadMatch[4]?"floatingPanel":"sliderPanel";
var panelID=lookaheadMatch[9]; if (panelID) panelID=panelID.slice(1,-1); // trim off delimiters
var panel=createTiddlyElement(place,"div",panelID,panelClass,null);
panel.button = btn; // so the slider panel know which button it belongs to
btn.sliderPanel=panel; // so the button knows which slider panel it belongs to
panel.defaultPanelWidth=(lookaheadMatch[4] && lookaheadMatch[4].length>2)?lookaheadMatch[4].slice(1,-1):"";
panel.setAttribute("transient",lookaheadMatch[5]=="*"?"true":"false");
panel.style.display = show;
panel.style.width=panel.defaultPanelWidth;
panel.onmouseover=function(event) // mouseover on panel aligns floater position with button
{ if (window.adjustSliderPos) window.adjustSliderPos(this.parentNode,this.button,this); }
// render slider (or defer until shown)
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
if ((show=="block")||!lookaheadMatch[11]) {
// render now if panel is supposed to be shown or NOT deferred rendering
w.subWikify(lookaheadMatch[10]?createTiddlyElement(panel,"blockquote"):panel,this.terminator);
// align floater position with button
if (window.adjustSliderPos) window.adjustSliderPos(place,btn,panel);
}
else {
var src = w.source.substr(w.nextMatch);
var endpos=findMatchingDelimiter(src,"+++","===");
panel.setAttribute("raw",src.substr(0,endpos));
panel.setAttribute("blockquote",lookaheadMatch[10]?"true":"false");
panel.setAttribute("rendered","false");
w.nextMatch += endpos+3;
if (w.source.substr(w.nextMatch,1)=="\n") w.nextMatch++;
if (config.options.chkDebugLazySliderDefer) alert("deferred '"+title+"':\n\n"+panel.getAttribute("raw"));
}
}
}
}
)
// TBD: ignore 'quoted' delimiters (e.g., "{{{+++foo===}}}" isn't really a slider)
function findMatchingDelimiter(src,starttext,endtext) {
var startpos = 0;
var endpos = src.indexOf(endtext);
// check for nested delimiters
while (src.substring(startpos,endpos-1).indexOf(starttext)!=-1) {
// count number of nested 'starts'
var startcount=0;
var temp = src.substring(startpos,endpos-1);
var pos=temp.indexOf(starttext);
while (pos!=-1) { startcount++; pos=temp.indexOf(starttext,pos+starttext.length); }
// set up to check for additional 'starts' after adjusting endpos
startpos=endpos+endtext.length;
// find endpos for corresponding number of matching 'ends'
while (startcount && endpos!=-1) {
endpos = src.indexOf(endtext,endpos+endtext.length);
startcount--;
}
}
return (endpos==-1)?src.length:endpos;
}
//}}}
//{{{
window.onClickNestedSlider=function(e)
{
if (!e) var e = window.event;
var theTarget = resolveTarget(e);
var theLabel = theTarget.firstChild.data;
var theSlider = theTarget.sliderPanel
var isOpen = theSlider.style.display!="none";
// toggle label
theTarget.innerHTML=isOpen?theTarget.getAttribute("closedText"):theTarget.getAttribute("openedText");
// toggle tooltip
theTarget.setAttribute("title",isOpen?theTarget.getAttribute("closedTip"):theTarget.getAttribute("openedTip"));
// deferred rendering (if needed)
if (theSlider.getAttribute("rendered")=="false") {
if (config.options.chkDebugLazySliderRender)
alert("rendering '"+theLabel+"':\n\n"+theSlider.getAttribute("raw"));
var place=theSlider;
if (theSlider.getAttribute("blockquote")=="true")
place=createTiddlyElement(place,"blockquote");
wikify(theSlider.getAttribute("raw"),place);
theSlider.setAttribute("rendered","true");
}
// show/hide the slider
if(config.options.chkAnimate && (!hasClass(theSlider,'floatingPanel') || config.options.chkFloatingSlidersAnimate))
anim.startAnimating(new Slider(theSlider,!isOpen,e.shiftKey || e.altKey,"none"));
else
theSlider.style.display = isOpen ? "none" : "block";
// reset to default width (might have been changed via plugin code)
theSlider.style.width=theSlider.defaultPanelWidth;
// align floater panel position with target button
if (!isOpen && window.adjustSliderPos) window.adjustSliderPos(theSlider.parentNode,theTarget,theSlider);
// if showing panel, set focus to first 'focus-able' element in panel
if (theSlider.style.display!="none") {
var ctrls=theSlider.getElementsByTagName("*");
for (var c=0; c<ctrls.length; c++) {
var t=ctrls[c].tagName.toLowerCase();
if ((t=="input" && ctrls[c].type!="hidden") || t=="textarea" || t=="select")
{ ctrls[c].focus(); break; }
}
}
var cookie=theTarget.sliderCookie;
if (cookie && cookie.length) {
config.options[cookie]=!isOpen;
if (config.options[cookie]!=theTarget.defOpen)
saveOptionCookie(cookie);
else { // remove cookie if slider is in default display state
var ex=new Date(); ex.setTime(ex.getTime()-1000);
document.cookie = cookie+"=novalue; path=/; expires="+ex.toGMTString();
}
}
// prevent SHIFT-CLICK from being processed by browser (opens blank window... yuck!)
// but allow plain click to bubble up to page background (to dismiss open popup, if any)
if (e.shiftKey) { e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); }
return false;
}
//}}}
//{{{
// click in document background closes transient panels
document.nestedSliders_savedOnClick=document.onclick;
document.onclick=function(ev) { if (!ev) var ev=window.event; var target=resolveTarget(ev);
// call original click handler
if (document.nestedSliders_savedOnClick)
var retval=document.nestedSliders_savedOnClick.apply(this,arguments);
// if click was inside transient panel (or something contained by a transient panel)... leave it alone
var p=target;
while (p)
if ((hasClass(p,"floatingPanel")||hasClass(p,"sliderPanel"))&&p.getAttribute("transient")=="true") break;
else p=p.parentNode;
if (p) return retval;
// otherwise, find and close all transient panels...
var all=document.all?document.all:document.getElementsByTagName("DIV");
for (var i=0; i<all.length; i++) {
// if it is not a transient panel, or the click was on the button that opened this panel, don't close it.
if (all[i].getAttribute("transient")!="true" || all[i].button==target) continue;
// otherwise, if the panel is currently visible, close it by clicking it's button
if (all[i].style.display!="none") window.onClickNestedSlider({target:all[i].button})
}
return retval;
};
//}}}
//{{{
// adjust floating panel position based on button position
if (window.adjustSliderPos==undefined) window.adjustSliderPos=function(place,btn,panel) {
if (hasClass(panel,"floatingPanel")) {
var left=0;
var top=btn.offsetHeight;
if (place.style.position!="relative") {
var left=findPosX(btn);
var top=findPosY(btn)+btn.offsetHeight;
var p=place; while (p && !hasClass(p,'floatingPanel')) p=p.parentNode;
if (p) { left-=findPosX(p); top-=findPosY(p); }
}
if (findPosX(btn)+panel.offsetWidth > getWindowWidth()) // adjust position to stay inside right window edge
left-=findPosX(btn)+panel.offsetWidth-getWindowWidth()+15; // add extra 15px 'fudge factor'
panel.style.left=left+"px"; panel.style.top=top+"px";
}
}
function getWindowWidth() {
if(document.width!=undefined)
return document.width; // moz (FF)
if(document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )
return document.documentElement.clientWidth; // IE6
if(document.body && ( document.body.clientWidth || document.body.clientHeight ) )
return document.body.clientWidth; // IE4
if(window.innerWidth!=undefined)
return window.innerWidth; // IE - general
return 0; // unknown
}
//}}}
//{{{
// TW2.1 and earlier:
// hijack Slider animation handler 'stop' handler so overflow is visible after animation has completed
Slider.prototype.coreStop = Slider.prototype.stop;
Slider.prototype.stop = function()
{ this.coreStop.apply(this,arguments); this.element.style.overflow = "visible"; }
// TW2.2+
// hijack start/stop handlers so overflow style is saved and restored after animation has completed
if (version.major+.1*version.minor+.01*version.revision>=2.2) {
/**
Animator.prototype.core_startAnimating = Animator.prototype.startAnimating;
Animator.prototype.startAnimating = function() {
for(var t=0; t<arguments.length; t++)
arguments[t].element.save_overflow=arguments[t].element.style.overflow;
return this.core_startAnimating.apply(this,arguments);
};
**/
Morpher.prototype.coreStop = Morpher.prototype.stop;
Morpher.prototype.stop = function() {
this.coreStop.apply(this,arguments);
this.element.style.overflow = this.element.save_overflow||"visible";
};
}
//}}}
An experiment in laboratory communication and teaching.
D Gitelman
^^(d DASH gitelman AT northwestern DOT edu)^^
Key for Neutal Density Filters
| !Filter Factor (~NDx) | !Filter Density | !f-Stop Reduction |
|2 | 0.3 | 1 |
|4 | 0.6 | 2 |
|8 | 0.9 | 3 |
|64 | 1.8 | 6 |
|1,000 | 3.0 | 10 |
|10,000 | 4.0 | 13 |
|1,000,000 | 6.0 | 20 |
Nice selection of filters available from: http://www.bhphotovideo.com/
2011-07-12: Updated [[getpsth]] to work with SPM8.
2010-11-10: Updated [[mip_maker]] function
2010-11-10: Updated [[nifti2ana]] function
2008-11-28: Added [[img2nii]] function
2008-08-19: Added nbil_defaultcmap code to [[nbil_mosaic]]
2008-07-22: Fixed bug in [[nifti2ana]] function.
2008-05-28: Changed name of nbilmosaic to [[nbil_mosaic]]. Some bugfixes
2008-04-27: Added some DCMNotes
2008-04-24: Fixed [[nbilmosaic]] to be self-contained, updated variables. //I don't know how this worked at all before.//
2008-04-11: Fixed bug in [[getpsth]] line 288, which did not include brackets around a vector. Thanks Simon Glynn.
2008-03-09: [[SPMWithinSubjectsDesigns]] updated with a [[link|http://d.gitelman.googlepages.com/conweights.pdf]] to a pdf about contrasts for within subjects designs in SPM.
2008-03-09: [[SPMWithinSubjectsDesigns]] tiddler added. It elegantly explains how to setup within subjects designs in SPM. (from Jan Gläscher). Updated TiddlyWiki version to 2.3.
2007-12-14: [[getpsth]] v. 1.22, fixed error in indexing the design matrix.
2007-09-15: Modified [[getpsth]] to potentially work with SPM99. Actually all that was done was to make the calls to spm_get work. I am not sure the function will actually work as I did not check the format of SPM99's SPM.mat files first.
2007-08-23: Added [[nbil_cleanpath]]. The toolbox formerly known as CBMGtools is now officially (I am not sure who makes these things official): NBILTools.
2007-08-18: Added function LowPassFilterWithSPM99
2007-08-06: Added function [[retVer]] for returning a version string from a file under source code control. Added instructions for [[RemoteSVN]] allowing one to have access to a protected SVN site by tunneling through an allowed system.
2007-05-25: Fixed instructions for [[meanImage]] tiddler. The function should be put in a directory called mean_img. If it is put in a directory called meanimg then an entry appears in the Toolbox menu. This does not work. It must be accessed via the Tasks -> Tools batch menus.
2007-05-16: Added [[meanImage]] tiddler for averaging images.
2007-03-20: Fixed link to [[resize_img]]. Thanks Anna Barnes!
2007-03-18: Fixed bug in [[get_psth]] function. Needed to add sem field to PSTH.events structure. Thanks to Jan Gläscher.
2007-03-05: Added [[resize_img]] as a better function for resliceing images. Updated IMAGING-SOFTWARE sites. Added [[mip_maker]] for plotting points.
2007-02-07: Added [[UpdateImageTransform]] and [[4x4TransformationMatrix]] methods tiddlers.
2007-01-01: Added [[getclustdata]] function. This is useful to get data from an image file based on the voxels in a selected cluster.
2006-12-27: Added [[smoothspmimage]] function (based on example from C Gaser on SPM list).
2006-12-12: Added [[mksphroi]] function and methods regarding calculation of FWHM images.
2006-12-01: Note to Readers: In order to use Comments you must must give yourself a name in the options panel. If the options panel is not visible, click on options to the right. Put in your name, a nickname or initials instead of "YourName." If you want to comment on an open tiddler, close it, then open it again, after you put in your name.
2006-11-28 fixed path for [[unwrapSlab]] function; added [[setslices2zero]] function.
2006-11-26: added [[unwrapSlab]] function and UnwrapSlabInstructions. Added commenting via [[Haloscan|http://www.haloscan.com]]. This will allow visitors to comment on code, make suggestions, etc.
2006-11-25: updated extractbrain function for SPM5. Renamed as [[cbmg_config_extractbrain.m|extractbrain for SPM5]]
2006-11-25: multiple updates to all plugins to get the site working properly again.
2006-11-24: CBMG.TIDDLYSPOT.COM upgraded to Tiddlywiki version 2.1
2006-11-24: get_psth upated to work with SPM5., NU-SPMforDummies calendar updated.
2006-11-02: NU slice order function updated. Please see the [[nusliceorder|nusliceorder]] function for more info.
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date("Apr 19, 2007"),
source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
coreVersion: '2.2.0 (Beta 5)'
};
config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");
merge(config.macros.option.types, {
'pas': {
elementType: "input",
valueField: "value",
eventName: "onkeyup",
className: "pasOptionInput",
typeValue: config.macros.option.passwordInputType,
create: function(place,type,opt,className,desc) {
// password field
config.macros.option.genericCreate(place,'pas',opt,className,desc);
// checkbox linked with this password "save this password on this computer"
config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);
// text savePasswordCheckboxLabel
place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
},
onChange: config.macros.option.genericOnChange
}
});
merge(config.optionHandlers['chk'], {
get: function(name) {
// is there an option linked with this chk ?
var opt = name.substr(3);
if (config.options[opt])
saveOptionCookie(opt);
return config.options[name] ? "true" : "false";
}
});
merge(config.optionHandlers, {
'pas': {
get: function(name) {
if (config.options["chk"+name]) {
return encodeCookie(config.options[name].toString());
} else {
return "";
}
},
set: function(name,value) {config.options[name] = decodeCookie(value);}
}
});
// need to reload options to load passwordOptions
loadOptionsCookie();
/*
if (!config.options['pasPassword'])
config.options['pasPassword'] = '';
merge(config.optionsDesc,{
pasPassword: "Test password"
});
*/
//}}}
<part Copying>
COPYING
#For the most part, active data will be put in /data/studies. This is the hard drive on Hogwarts, the server. All computers have access to it. Each study will have a folder (study_directory) that will contain all subject data related to that study. The processes will run faster if you can store the data on the same machine that you are going to be working on because then the information will not have to travel over the network. Thus if you have a super super huge processing job to perform on data stored on Hogwarts, there might be an advantage to logging on to Hogwarts to do this job. Each of the workstations also has a local hard drive (buckbeak1, hagrid1) that will be primarily used for data backup.
#Go into your study directory by typing cd /data/studies/study_directory. Create directory for your subject and name it your subject’s ID number (mkdir subject_ID ) and then create another directory called raw within your subject directory.
#Copy data from CD into raw on to system and check that all files are copied. For instance, from a terminal window type cd /mnt/cdrom so that you are in the CD. When you type ls, you should see a bunch of files named something like MR.1.3.12…
**To copy the files from the CD to the hardrive, type:
***@@cp * /data/studies/study_directory/subject_ID/raw or whatever the pathname to your raw directory is.@@
**This might take several minutes.
#cd into the raw directory if you are not already there
#You will have to change the permissions of the files you just copied to ensure that you (the user) has the appropriate permission to read and write the files.
**@@chmod 774 //filenames//@@ will give user and group read/write/execute permission and others will have no permissions.
#To open Matlab, you must be in your home directory so that Matlab can read the startup.m file located there to properly open Matlab. If you are NOT in your home directory, Matlab will open but you won’t be able to open SPM2. Therefore…
#Open a new terminal window (which will automatically start you off in your home directory) or in an existing window type cd (which will take you to your home directory).
#Start Matlab
**Type: @@matlab -nodesktop@@ to start without the Matlab desktop. //Note: This may depend on your system configuration.//
**Type: @@matlab -nojvm@@ to start without java.
#spm to open SPM2.
#fmri time-series
#IMPORTANT: You must be sure to change the present working directory in SPM (or Matlab terminal window, they are the same thing) to your subject’s directory, e.g. /data/studies/study_directory/subject_ID. In the course of processing the data, SPM will create files and always saves them to whatever is the present working directory. If you opened Matlab from your home directory and don’t change directories within the Matlab window or SPM, all those files will be saved in your home directory.
</part>
<part Conversion>
Converting DICOM to SPM format
#Click on Toolboxes and select DICOM to convert from DICOM to Analyze format.
#An spm_get window will pop up asking you for your DICOM Working Directory.
**This is the place where your raw data (DICOM) is stored, i.e. your raw directory. See below for maneuvering in the window.
**When you are inside your raw directory, click the single black "." to select it, then hit Done.
#To select your DICOM files, go into your raw directory (so that you can see all your raw files listed below) and hit all. All of the raw files should now be selected. Click Done.
#The spm_dicom function will convert the files and rename. Each raw functional file = 1 volume and gives 3 Analyze files:
**Image .img
**Header .hdr - info about that image
**4x4 matrix .mat - says how image is oriented in 3D space
#The function will append an "f" to the name of your functional files, which begin with the patient ID entered at the time of scanning. Patient IDs begin with “tr” followed by the MRRC study ID (e.g. 1234), so the functional files will be renamed as
"ftr1234-series#-image#-acquisition#"
#All structural raw files are compiled into 1 volume (each T1 file = 1 slice). An "s" will appended to the name so T1 files will start with "str…"
Using the spm_get window: The red names on the left are the subdirectories inside your present location. Click on a subdirectory name to go into that subdirectory. Click on the red ".." to move up one level in the file tree hierarchy. In the center in black are the files inside your present location. Click on one of them to select it. Click on the single black "." to select the directory you are in. At the top there is a drop-down menu where you can quickly go to previously accessed directories. Click "pwd" in the upper right corner to go to your present working directory.
</part>
<part Sorting>
SORTING VIA [[SORTFILES]] PROGRAM
NOTE: This process may also be done manually if the program doesn't work: create directories for each of your runs (including T1) using mkdir directory_name and then move (mv) the appropriate series into the run folder. For instance, if series #2 is run1, make a directory in your subject folder called run1 , go back into your raw directory, and then type mv fstr1234-0002* ../run1 (i.e. move files starting with that string up one level in the hierarchy and into the directory called "run1").
**** If you do this sorting procedure by hand, you must remember to manually DELETE THE DUMMY FILES (both the .img and .hdr files)!! ****
1. Toolboxes -> CBMG tools
2. Click on sort files
3. Select the base directory. This is the directory where you want your files sorted. For example, if your raw data is in /data/studies/congruency/F100/raw, you would select the directory /data/studies/congruency/F100. Click done.
4. Type in the # of runs, including the anatomicals (t2localizer, t1flash, t13D)
5. Then for each run, it will ask you the series number (get from Trio run sheet), the # of dummy volumes, and the name of the directory you want to put it in. For the anatomicals, # of dummies = 0!
6. Then it will ask you to select all the images. Select both the functionals and the anatomicals (ftr and str files).
7. The program will then sort the files. It takes about 15 seconds to sort ~1000 files.
</part>
PRE-PROCESSING
SLICE TIMING CORRECTION
At the 3T Trio magnet, slices are acquired from bottom up in interleaved fashion throughout TR. If there is an even number of slices, it will acquire slice numbers 2, 4, 6... then 1, 3, 5... If there are an odd number of slices then it acquires 1, 3, 5,... 2, 4, 6...SPM considers slice 1 to be at the bottom. The purpose of the interleaved order is to minimize "cross-talk" between slice selection pulses - e.g. slice 2 is partially excited when acquiring slice 1 so you don't want to measure slice 2 right away. Thus, slice timing corrections shift signal so that it is as if all the slices were acquired at the same time (at 1/2 TR).
Because the different points in the volume are scanned at slightly different times, the model fitting later on will not be optimal. Slice-time correction imporves the fit by adjusting the voxel’s time series so that it appears that all voxels were scanned at the same time. This is achieved by phase shifting the time series values at each voxel (sliding the 1D time plot forwards or backwards).
The following may or may not be true, I’m just repeating what some have said: If you have a block design, you may not want to do this step because several scans are averaged together when analyzing a block design. Thus any gain in "accuracy" from interpolating the data from slices within one scan might be lost in the process of averaging across scans. ??Because it involves interpolating the signal to a time other than when it was acquired and measured, this step can "deteriorate" the data.??
1. Slice-timing
2. # of subjects or sessions = # functional runs
3. Select each session's scans separately (that is, press done after selecting the "ffmri" .img files for each session). Another spm_get window will pop up to get the next session's scans. The order of sessions does not matter (but in generally it's good to keep a consistent alphabetical/numerical order, i.e. going from top to bottom as the directories are showed on the spm_get window).
4. Sequence type - user specified
5. Order of slices (slice 1 = bottom)
For an ODD number of slices (max# = N), the order is 1:2:N 2:2:N-1
For an EVEN number of slices (max# = N), the order is 2:2:N 1:2:N-1
This says that for an odd slice number, start at slice 1 and count up by 2 until you get to slice N, then go back to 2 and count up by 2 until you reach slice N-1. This is because at the Trio, we acquire data from foot to head in an interleaved fashion.
(For data acquired on Northwestern (NU) 3T Trio, slice-timing is different!
NU: For an ODD number of slices (max# = N), the order is 2:2:N-1 1:2:N
(Reference slice = 1)
NU: For an EVEN number of slices (max# = N), the order is 1:2:N-1 2:2:N
(Reference slice = 2 or N-1)
6. Reference slice = N (i.e. the slice taken at 1/2 TR)
For an odd # of slices, slice N is acquired at 1/2 TR. For an even # of slices, slice N and slice 1 are equally in the middle so either one can be chosen, but it's easier to remember to choose N regardless of slice number.
7. Interscan interval = your TR
8. Acquisition time = how long it actually takes to acquire your number of slices (N) = TR - TR/N. It figures it out for you so just accept whatever value it puts up.
9. Creates "aftr..." files – the “a” is for acquisition corrected. For each volume there will be .img, .hdr, and .mat files.
REALIGNMENT
The realignment procedure figures out how to move each image volume so that it lines up spatially with other image volumes. This acts to minimize the effects of a subject’s head movements. Note that if head movements are > 1 mm between scans within a run. or if head movements are correlated with the task (e.g., more movements occur when the subjects responds) this introduces additional complications in the preprocessing data analysis. The realignment algorithm assumes that the movement is taking place between image volumes. This is not quite correct since subjects can also move between slices, but it is a reasonable approximation. The realignment procedure is used within modalities (e.g. across functional volumes, NOT between functional and structural). Instructions in step 4 are for realigning to the functional volume taken closest to the T1. If you don't care what volume the functionals are realigned to, skip step 4. According to DRG, it is currently just easier and better to realign to the mean image of all the realigned volumes rather than worrying about which scan was taken closest to the T1.
If you are performing an experiment in which data are collected over two days (e.g. capsaicin study) you need to make sure you realign all the data to the same functional volume. In other words all the EPI data must be realigned together.
1. Realign
2. # subjects = 1
3. # sessions = # functional runs
4. TO REALIGN TO VOLUME CLOSEST TO T1 IMAGE:
a) It will ask you for the scans to realign. The first volume you choose will be the reference volume to which the other functionals will be realigned. Thus we want to first choose the volume taken closest to the T1 anatomical image. Go to the functional session immediately preceding the T1 session. If the T1 was the first series, go to the functional session immediately following it.
b) We want to select the "aftr" .img files. Click on the white number next to the "aftr" .img files - this will list out each volume separately.
c) If this is the session before the T1 scroll to the bottom and select the last volume in the series. If this is the session after the T1, select the first volume in the series. In other words, choose the volume acquired closest in time to the T1. This is what all other functionals in this session and in the remaining sessions will be aligned to.
d) Then click all to select the rest of the volumes in the series.
e) Click done because you have selected all the volumes for the first series.
f) Now for each of the remaining sessions, go to that directory, choose the "aftr" .img files, and hit done. For these remaining sessions, you can select all of the .img files within one session and the order of sessions should not matter.
g) Skip to Step 6.
5. IF YOU DON'T CARE WHAT VOLUME YOU REALIGN TO:
a) Go into a functional run directory (It doesn't matter which one, though it is nice to be consistent and start at the top of the alphabetical/numerical list in the spm_get window. This way you'll know later on which volume you realigned to in case you want to know).
b) Click on the "aftr" .img files to select them all, then press done. All the functionals will be realigned to the first volume in this session.
c) For each of the remaining sessions, go to the appropriate directory, select the "aftr" files, and click done. Again, the order in which you select the directories does not matter.
6. Which option - coregister and reslice
7. What do you want to create? Mean image only
A mean image of all functional volumes will be created and used to co-register with the T1. Only after coregistration and the determination of the normalization parameters will a new set of .IMAGE files be created for the functionals
8. Background information about reslice interpolation method (this is just for your
edification, this is not necessary to do the analysis) : through the defaults, one can choose between nearest-neighbor, trilinear, various B-splines and fourier. DRG says: Never choose nearest neighbor unless you don’t care about the results. Only choose fourier if the image volume have been acquired with isotropic voxels sizes (i.e., 2x2x2 or 3x3x3). Trilinear can be used if the volumes won’t be normalized and resliced again. The best choice is 4th degree B-spline.
9. What is happening: First it realigns each volume WITHIN the session to the first file
selected in each session. To do this it figures out how each volume should be moved and puts that in the *.mat file for each volume. Similarly the algorithm can figure out how each session should be moved to line up with the preceding session.. In the end, the .mat files of each volume will be adjusted so that all functionals are in the same 3D space. The procedure will also create an “rp…txt” file, which contains the information used for realignment in a text format.
COREGISTRATION
Line up the functional and T1 volumes by pulling T1 into the space of the functionals (easier to adjust one T1 volume than 2000+ functional volumes). It can work by segmenting the volumes into gray and white matter and then matching up the segments and figuring out the parameters necessary to rotate/translate/squeeze/pull those segments into alignment. However, SPM2 is currently set to use a mutual information algorithm rather than the segmentation algorithm. The end result for the user is the same.
1. Coregister
2. # subjects = 1
3. Coregister only
- if you are only looking at a single-subject and will not be normalizing, you can choose coregister and reslice; this will create an "rstr" anatomical image that is resliced into the space of the functionals. This should not be normalized.
4. Select target image = what you want to shoot towards
- choose the mean...img image file just created in realignment (probably in your first functional run directory).
5. Source image = what you want to move into target space
- select the T1 .img file (str…img) from the t13D folder.
6. It will ask for other images - don't select anything - click done.
7. The coregistration procedure will alter the T1 .mat file
SEGMENTATION
This is an optional step that is now part of our routine procedure. We did this because we had problems normalizing our brains with the T1.mnc template. Therefore what you should now do is segment the T1, determine normalization parameters by matching your gray matter segment (seg1) to the apriori gray matter template, and then apply those parameters to your functional and T1 data. This procedure is detailed in the steps that follow. If you want to do it the old way, skip segmentation, go straight to normalization, do not pass go, and follow the yellow brick road (i.e. the ► instructions).
Segmentation is also a step to take if you want to do voxel-based morphometry (VBM).
To segment the T1 into gray matter (seg1), white matter (seg2), and CSF (seg3):
1. click Segment
2. Select the T1 str . . .img file.
3. Subj 2? click done.
4. Already spatially normalized - no
5. Modality - T1 MRI
NORMALIZATION
Warp functionals/T1 into the space of template brain. First it minimizes differences via translation, rotation, zooming (affine movement). Then it performs 3D cosine transformations to match curves (e.g. sulcal differences). Thus, it is a mathematical algorithm and is not based on landmarks. Three steps you have to perform:
1. Determine parameters needed to warp your (co-registered) T1 to the template
2. Apply those parameters to warp your functionals
3. Apply those parameters to warp your T1 (note that in step 1, T1 not actually resliced). Steps 2 and 3 can be done in any order
Step 1 - determine parameters
1. Normalize -> Determine parameters only
2. Template image = gray.mnc in /usr/contrib/spm/apriori (you will automaticall be sent to /usr/contrib/spm/templates, so first go up one level, go into the “apriori” folder, and then select “gray.mnc”.
► To do it the old way, select the T1.mnc in the /usr/contrib/templates directory.
3. Source image = your str…_seg1.img file in your T1 folder. This is your segmented gray matter.
► To do it the old way, select your str…img file (non-segmented)
4. Source image 2 = none. Just click done
Step 2 - normalize functionals
Before applying those parameters, you must change some of the default normalization parameters. Some of these changes are used for both functionals and anatomicals, but the voxel size depends on what you are normalizing. For functionals, you should check that the voxel size used is 3 x 3 x 3. The default value as of this writing is 2 x 2 x 2. When normalizing the T1, however, the voxel size MUST be changed to 1 x 1 x 1. SPM99 used to ask for this number within the procedure itself, but for some reason this has been moved to the defaults in SPM2. Don’t forget to make sure your images are resliced at the correct resolution.
5. Changing Defaults Procedure:
Note: the option with the * is the default.
Defaults -> Spatial normalization -> Writing normalized
1. *Preserve concentrations
2. Bounding box? -90:90 -126:90 -72:108 (Template). This may not be absolutely necessary, but it seems to work best. If you choose it for one subject in your study, you must choose this for all.
► In the old way, we kept this option as -78:78-112:76-50:85 (Default).
3. Voxel size? Choose 3 x 3 x 3 if you are applying the parameters to the functionals.
4. Interpolation method? 4th degree B-spline.
5. Way to wrap images? *No wrap
6. Normalize -> Write normalized only
7. Parameters, subject 1 = what the program just figured out, the parameters needed to move T1 to template space. Select the str…_seg1_sn.mat file in T1 directory
- To do it the old way, select the sftr…_sn.mat file in the T1 directory.
8. Images to write - select aftr...img image files for each session ALL TOGETHER (order does not matter). Then press done.
9. It will ask for subject 2, just select done.
10. This will created “waftr..” files = w for warped
Step 3 - normalize T1
11. Since we are now normalizing the T1, the voxel size must be 1 x 1 x 1.
12. Go through the Changing Defaults Procedure section again (# 6 in Step 2 above), but select voxel size = 1 x 1 x 1
13. Repeat the Normalize -> Write normalized only instructions but select the mstr.img file in your t13D folder as the image to write. This mstr file was created after segmentation and has been bias corrected.
SMOOTHING
FWHM of a Gaussian distribution = the distance from the mean to the point where the probability density function drops from 1.0 to 0.5 (half of the maximum value). The wider the kernel the greater the smoothing and the larger impact neighboring voxels have on each other. Smoothing increases sensitivity by averaging out uncorrelated noise across voxels, but reduces spatial resolution by blurring small areas of activity across the area over which it was smoothed. A positive side effect of the blurring is that cross-subject averaging is less affected by inter-subject anatomical variability.
Spatial blurring reduces noise via local averaging, so the noise values in the local neighbourhood tend to cancel each other out. However, in order for the underlying signal to not be reduced along with the noise, it is required that the extent of the blurring is not larger than the size of the activated region; if very small activations are expected then spatial filtering should be reduced or not carried out. We may wish to go with 7mm kernel when the amygdala is an R01.
Spatial blurring is also carried out to meet requirements of random field theory (4mm Kernel is adequate to do this).
We want to smooth the functionals, NOT the T1 anatomical.
1. Smooth
2. 10 mm FWHM kernel is usually used, especially for an older population where there will be a lot of anatomical variation and if you are looking for larger cortical activations. For a younger population and/or if you are looking for smaller activations in areas like the hippocampus or amygdala, a 7mm kernel may be appropriate.
3. Select waftr...img functional images from each session ALL TOGETHER, then click done
4. Creates "swaftr..." images - s for smoothed
* After pre-processing, an spm2.ps file will be created that graphical illustrations of each of the preprocessing steps. For the realignment procedure the amount of movement will be displayed. For the coregistration and normalization procedures an example of the results will be displayed. This can be viewed with Ghostview (see section on viewing .ps files on page 2).
27/09/06 19:45
I also did an analysis using an HRF of 256 secs.
The original spm2 analysis has more voxels at a much higher significance level. However, this is the effect of the global and not the HRF specification, since analyses with HPF of 128 vs. 256 look pretty much the same.
The parameter estimates are also a bit higher for the spm2 analysis. Below is a bar graph of the parameter estimates from the 3 analyses over all effects. The spm5 analyses are lower, but again I think this is a global issues. You can probably go with a HPF of 128. Don't worry so much about the individual results, since you are going to examine them at a group level anyways.
[img[bar graph of parameters est for all 3 analyses|http://d.gitelman.googlepages.com/betabar-prime.jpg]]
The reminder macro can take the following arguments.
!!!!date syntax
* @@{{{year:NUMBER}}}@@ - The four digit representation of the year (for example {{{year:2046}}} or {{{year:1999}}}
* @@{{{month:NUMBER}}}@@ - The numerical representation of the month (for example {{{month:1}}} for January, {{{month:12}}} for December)
* @@{{{day:NUMBER}}}@@ - The numerical representation of the day of the month (for example {{{day:15}}} will match the 15th day of the month)
* @@{{{dayofweek:NUMBER}}}@@ - The numerical representation of the day of the week. Valid values are in the range of 0-6. {{{dayofweek:0}}} will match Sunday, and {{{dayofweek:6}}} will match Saturday.
!!!!offsets
* @@{{{offsetdayofweek:NUMBER}}}@@ - The numerical representation of a day of the week. Valid values are in the range of 0-6. 0 will match Sunday, and 6 will match Saturday. If offsetdayofweek is specified, the year, month, day and dayofweek will be matched as usual, and the reminder will be set to the next occurence of the day of the week specified by offsetdayofweek. For example, the first Thursday of the month can be specified as {{{day:1 offsetdayofweek:4}}} and the second Thursday can be specified as {{{day:8 offsetdayofweek4}}} If offsetdayofweek is negative, the search will be performed backward. For example, the last Thursday in August can be found by {{{month:8 day:31 offsetdayofweek:-4}}}
* @@{{{recurdays:NUMBER}}}@@ - If recurdays is set, then the reminder will fire on the base date specified by year, month, day, and dayofweek and also every N days afterward. For example, if the reminder is specified with {{{year:2005 month:8 day:16 recurdays:2}}} it will match August 16, 18, 20, etc. Please make sure that you fully specify year, month and day in any recurring reminder.
!!!!leadtime
* @@{{{leadtime:NUMBER}}}@@ - Use this to specify when this reminder will appear in [[showReminders]]. If a reminder has a leadtime of 2, it will only show up in showReminders if it will be matched in the next two days. Likewise, a reminder with a leadtime of 60 will show up in showReminders even if showReminders has a lower leadtime. showReminders can override this behavior with the limit argument.
!!!!Reminder display options
* @@{{{title:"STRING"}}}@@ - A string used to identify this reminder when it is shown in a list of reminders. For example, {{{title:"New Year's Day"}}} or {{{title:"Elvis' Birthday"}}}. You can put standard TiddlyWiki formatting in the title.
* @@{{{format:"STRING"}}}@@ - Use this argument to override the default string used for display. You can put standard TiddlyWiki formatting in the format. The following substitutions will be made in the string before it is displayed.
** DIFF will be replaced with the one of the strings "Today", "Tommorrow", or "N days", where N is the number of days between now and the date of the reminder.
** TITLE will be replaced with the title of the reminder
** DATE will be replaced with the matched date of the reminder.
** ANNIVERSARY will be replaced with the number of years since between the matched date and firstyear
The default string is "DIFF: TITLE on DATE ANNIVERSARY"
* @@{{{firstyear:NUMBER}}}@@ - The first year that a reminder occurred, in four digit format. For example {{{firstyear:2001}}}. This is used when calculating the number of years that a reminder has happened.
* @@{{{hidden}}}@@ - If this option is present, the reminder will not be displayed in the regular view of the tiddler. You can use this to have reminders for [[displayTiddlersWithReminders]] to find, without having the countdown appear. See [[Season's Greetings example]] for an example.
Finally got SVN to work on my local machine allowing it to connect to a remote machine inside a firewall. This has taken far too long to sort out. I want to thank the individual who runs this blog: http://uucode.com/blog/2005/05/26/getting-access-to-a-firewalled-subversion-repository/#comment-8270
I don't know who you are, but thank you. There were some subtle changes needed from his instructions, so I have reproduced them below.
----
{{{
Initial data:
*svnserver: 192.168.x.yy (this is the actual svn server, ,which can only be accessed through fswerver)
*repo: directories to get to svn repository
*svn://svnserver/repo/ the repository URL for the local users
*fwserver: my.svnrepo.edu: Internet address of the firewall (i.e., machine through which you will access svnserver)
"Description"
Subversion can work over a tunnel, in particular, over a tunnel created by ssh. In this solution, clients think they establish a tunnel to my.svnrepo.edu: but in fact the tunnel continues to svnserver.
''Checking a local tunnel''
Checking that its possible to make a tunnel from fwserver to svnserver:
$ netcat 192.168.x.yy 3690
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
this is good so ^C and go onto next step.
''Making a special user''
//I did not do this as I am the only user on my system that access SVN//
Make a special user //svnuser// which will be used only for accessing Subversion. Don't set a password for svnuser. Authorization should be performed only through the ssh keys.
# useradd -c subversion access for user -m svnuser
"Key authentification"
It should be possible to login from an Internet computer to fwserver.dyndns.org as user svnuser without a password. This is described in the documentation for the ssh client Putty. Basically it involves setting up a set of SSH keys and placing the public key in the ~svnuser/.ssh/authorized_keys file on fwserver. See below.
There are many other tutorials for this on the net. Finding them is left as an exercise for the user.
"Making a tunnel"
Its possible to execute a command when an user authentificates using keys. The file ~svnuser/.ssh/authorized_keys should contain an entry like this:
command="/usr/bin/netcat 192.168.x.yyy 3690",no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAAB . . . . . . . 8Lw== user@client
Note the command entry. Other options add more security.
"Test the tunnel"
$ ssh svnuser@fwserver
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
^C Connection to fwserver closed.
$
"Test SVN access"
The tunnel is established, and now its possible to use the tunnel:
$ svn list svn+ssh://svnuser@fwserver/repo/
Folder/
Catalogue/
Directory/
----
"Some specifics for Windows"
These instructions pertain to using Putty.
* To generate keys, run the program puttygen.exe.
* Save the private key as svnuser.ppk.
* On fwserver, add the public key to the file ~svnuser/.ssh/authorized_keys.
* The data for this file is displayed in the text field Public key for pasting into OpenSSH authorized_keys file.
* Use the same set of options as for the Linux key.
"Check that channel works:"
C:\> C:\util\putty\plink.exe -i C:\util\putty\svnuser.ppk
svnuser@fwserver
Using username svnuser.
Server refused to allocate pty
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
"Configuring TortoiseSVN"
TortoiseSVN is a Windows GUI to (Sub)Version Control.
Run TortoiseSVN, the menu Settings. In the TortoiseSVN Settings/tt> window, on the Network tab, specify the following SSH client:
C:\util\putty\PLINK.EXE -i C:\util\putty\svnuser.ppk -batch
In order to test the connection, run the Repo-Browser. The repository URL is the same as under Linux:
svn+ssh://svnuser@fwserver/repo/
Unfortunately, black DOS windows appear while working with the repository. But its better than no access at all.
Once everything is running correctly, to get rid of the window use Tortoiseplink.
}}}
/***
|Name:|RenameTagsPlugin|
|Description:|Allows you to easily rename or delete tags across multiple tiddlers|
|Version:|3.0 ($Rev: 3861 $)|
|Date:|$Date: 2008-03-08 10:53:09 +1000 (Sat, 08 Mar 2008) $|
|Source:|http://mptw.tiddlyspot.com/#RenameTagsPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
Rename a tag and you will be prompted to rename it in all its tagged tiddlers.
***/
//{{{
config.renameTags = {
prompts: {
rename: "Rename the tag '%0' to '%1' in %2 tidder%3?",
remove: "Remove the tag '%0' from %1 tidder%2?"
},
removeTag: function(tag,tiddlers) {
store.suspendNotifications();
for (var i=0;i<tiddlers.length;i++) {
store.setTiddlerTag(tiddlers[i].title,false,tag);
}
store.resumeNotifications();
store.notifyAll();
},
renameTag: function(oldTag,newTag,tiddlers) {
store.suspendNotifications();
for (var i=0;i<tiddlers.length;i++) {
store.setTiddlerTag(tiddlers[i].title,false,oldTag); // remove old
store.setTiddlerTag(tiddlers[i].title,true,newTag); // add new
}
store.resumeNotifications();
store.notifyAll();
},
storeMethods: {
saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,
saveTiddler: function(title,newTitle,newBody,modifier,modified,tags,fields) {
if (title != newTitle) {
var tagged = this.getTaggedTiddlers(title);
if (tagged.length > 0) {
// then we are renaming a tag
if (confirm(config.renameTags.prompts.rename.format([title,newTitle,tagged.length,tagged.length>1?"s":""])))
config.renameTags.renameTag(title,newTitle,tagged);
if (!this.tiddlerExists(title) && newBody == "")
// dont create unwanted tiddler
return null;
}
}
return this.saveTiddler_orig_renameTags(title,newTitle,newBody,modifier,modified,tags,fields);
},
removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,
removeTiddler: function(title) {
var tagged = this.getTaggedTiddlers(title);
if (tagged.length > 0)
if (confirm(config.renameTags.prompts.remove.format([title,tagged.length,tagged.length>1?"s":""])))
config.renameTags.removeTag(title,tagged);
return this.removeTiddler_orig_renameTags(title);
}
},
init: function() {
merge(TiddlyWiki.prototype,this.storeMethods);
}
}
config.renameTags.init();
//}}}
________________________________________________________
CONTRASTS
It is often convenient to create a text document that lists all contrasts for a study. It is then easy to cut and paste these contrasts into the contrast manager window, thereby reducing the error of having to type them in each time. A contrast is built by "weighting" each condition.
- If you built your design matrix with the "hrf with time derivative" basis function, each condition will have two columns. The first represents the hrf, the second represents the time derivative. In our analyses we want to look at the hrf. The numbers in the contrast should sum to 0 (e.g. If your study has conditions A, B, and C in that order, the contrast A - (B+C) would translate into 2 0 -1 0 -1 0. Note that the time derivative column for column is assigned a 0.
- If you built your design matrix with the "hrf " basis set and included a regressor, the first column of each condition will represent the hrf alone and the second column will represent the regressor. Since you probably want to look at the activations correlated with the regression if you built the matrix this way, put a 0 for the first column and your numerical value for the second. For example, for A - (B+C), the contrast is 0 2 0 -1 0 -1.
- When defining a contast, each column of every session in the matrix must be specified. Thus, the pattern of numbers for one session described above (e.g. 1 0 1 0 -2 0) must be repeated for each session. You can use the "repmat" function to accurately build a contrast for a matrix that has multiple sessions. For instance, if your study has 4 sessions, each of which have the same 3 conditions, and you used the "hrf with time derivative" basis set, an example contrast A-(B+C) might look like 2 0 -1 0 -1 0 for one session. To define the contrast, you would type repmat([2 0 -1 0 -1 0],1,4). In other words, repeat that matrix in one row and 4 columns.
If you plan on doing random effects, you should make a spreadsheet where you write down the contrast number for each contrast for each subject. The contrast number appears in the contrast manager window next to the name of each contrast you build.
1. Click Results
2. Select SPM.mat file
3. Define new contrasts (under T contrasts tab)
4. Type in name of contrast
5. Specify contrast: can type in strings of numbers or use repmat function
- Remember that each column in the matrix must be assigned a number: SPM will right-pad the contrast with zeroes if necessary
- You can type in the numbers by hand or cut and paste from your contrasts text file (see above)
- Don't forget that each condition has two columns. You should use the first column (the hrf) and put a 0 for the second (the time derivative). Exception: if matrix was built with hrf alone and a regressor was included, you should use the second column (the regressor) and put a 0 for the first (the hrf alone).
6. Click done
7. If you are making a contrast spreadsheet so that you can do random effects later, be sure to note down the contrast number (number to the left of the contrast name in the contrast manager window).
8. Select the contrast you just built (or that you want to see) and click done
9. Mask with other contrasts
– usually no
– if you say yes, you’ll select which contrast you want to mask with and then be asked whether you want inclusive or exclusive masking. For more info on masking, see “One-way Anova and Masking” under “Intersubject analyses” on page 2.
– A time-saving shortcut for entering in a large number of contrasts and then estimating them at one time: After entering all your contrasts in the contrast manager window, select one of them and click done. Select yes to mask with other contrasts, and then select the rest of the contrasts that you want to specify. Choose inclusive or exclusive, it doesn’t matter. The result you get will be meaningless, but all of the chosen contrasts will be estimated.
10. Title - can keep default
11. Program will take some time to calculate...
12. p-value adjustments
Family-wise error (fwe) = threshold is corrected over # of voxels in volume: very stringent
FDR = false discovery rate
None - select this
13. p-value - usually start with 0.001. You can increase it to 0.005 or 0.01 to be less stringent.
14. extent threshold - 3 = at least 3 voxels must be clumped together. You can decrease this to 0 to be less stringent.
Output notes:
1. Click Results-Fig on graphics window to create satellite window.
2. Then click Volume in the lower left window to pull up activation peak statistics in that satellite window (can also click this when a satellite window is not open and the stats will come up on the main graphics window).
- Coordinates are clickable and crosshairs will jump to that location
- Click on p values to get full value in Matlab window
- P corrected = family wise error (see above)
- Cluster level : e.g. what is the significance of these 37 voxels given that there is a z=0.003 maxima within it?
3. Overlays -> Sections, then select your T1 (or mean) anatomical image on which activation will be laid. The three sections of the anatomical image are clickable.
4. Overlays -> Render will allow you to pull up 3D images with overlaid activation
5. Can right-click in upper half of graphics window and select "jump to local maxima." The crosshairs will then (you guessed it) jump to the local maxima.
6. Can brighten or darken image via Effects in graphics window
7. The crosshairs appear on the anatomical image by default. To turn them on, go to the Matlab command window and type spm_orthviews('Xhairs', 'off') . To turn them back on, type spm_orthviews('Xhairs', 'on') .
8. Small Volume Correction
- Center the crosshairs on your peak of interest (jump to local maxima)
- Click S.V.C. on square SPM2 window on lower left
- Select sphere at [coordinates]
- Radius of sphere: usually 10 mm, sometimes 15
- Statistics table will show in satellite figure; stats corrected the specified sphere
9. Can save to .ps file via Print. These will always be called "spm2.ps" but you can rename them later. If there is already a file with named spm2.ps in your current working directory, output will be appended to it.
Viewing .ps files and Printing
1. View .ps files with Ghostview
2. In a terminal window, type kghostview
3. Select the .ps file you want to view.
4. Printing: You can mark specific pages in the .ps file to be printed by using the menu commands (PageMarks) or left-clicking the little dot to the next to the page number on the tab on the left side of the window.
5. Print to color printer: press Print; choose hp3700dn (one-sided only!)
6. Print to black and white printer: press Print; choose Lexmark (one-sided only!)
7. To print from command line in regular terminal window (should work for any file):
[Note: -o sides=two-sided-long-edge is optional for printing double-sided]
Color printer: lpr -P hp3700dn –o sides = two-sided-long-edge filename_to_print
BW printer: lpr -P lexmark –o sides = two-sided-long-edge filename_to_print
________________________________________________________________________
Inter-subject Analyses
Mean image
Overlay inter-subject analyses on to mean image (averaged T1 of all subjects included in analysis).
- Be sure you are in the directory where you want your mean image saved.
- In SPM99/SPM2: Click on Toolboxes -> CBMGtools -> mean
- Select normalized T1 image ("wsfmri" .img) for each subject. When all are selected, press done
- mean.img and mean.hdr will be created in current working directory
- If the above “mean” function doesn’t work, you can create the mean image manually:
- Click on ImCalc [image calculation]
- Select T1 image
- Type in name that you want output file to be called (i.e. mean)
- Function: (i1+i2+i3...in)/n where n = # images. For example, if you want to average 5 subject's T1s, you should type (i1+i2+i3+i4+i5)/5
Fixed Effects
Create a new folder and build a design matrix just as you would for an individual subject, but put data from several subjects all together. You may have to adjust your vectors so that each subject's variables have individual names. If all variables are in one text file, one solution is to assign each subject a letter of the alphabet and than to prefix all of that subject's variables with its letter. If vectors consist of a column of numbers within a text file (no variables assigned within the file), give each subject's vector file a unique name. See section on loading vectors into a design matrix (page 2).
This analysis looks at what activation is present when averaging across subjects. It does not account for inter-subject variance (less stringent than random effects). Build contrasts just as you would for an individual subject.
Conjunction
Use the fixed effects matrix but when building contrasts, make the specified contrast for each individual. Then in the contrast manager window, highlight each individual subject's contrast all together (if all are next to each other, click on the blank white space to the right of the topmost individual contrast and with the mouse button pressed, drag down). On the right you should see the contrasts you've selected, one on top of the other. Click done and proceed just as you would for a regular contrast. Note that it will not ask you for a voxel threshold. This analysis accounts for inter-subject variance better than fixed effects.
Random Effects
Uses contrast images created for each subject. Shows activation only if present in each subject for that contrast. Similar to conjunction analysis in that it takes into account inter-subject variance. See DRG's 1999 review for a clear and very helpful explanation of some of the issues involved in random effects analyses: http://www.brain.northwestern.edu/cbmg/ranfx.html
1. Make a directory for that particular contrast and cd into it.
2. Basic Models -> 1 sample T-test
3. Select images: choose the con_00XX....img contrast image for each subject. Find contrast number (XX) from your contrast spreadsheet (see page2).
4. Grand mean scaling - no
5. Explicit mask - no
6. Global calculation - omit
7. Click the Estimate button on the main spm2 menu. Choose your random effects SPM.mat (be careful, the spm_get window will probably open to the last individual’s anal folder but you don’t want to select that subject’s SPM.mat file) then click Done.
8. Results - Select .mat file. This may take a few minutes to bring up the contrast manager window.
9. Define new contrast
- name it and put 1 into vector area
10. Continue just as with other contrasts...
NOTE: You can do the reverse random effects contrast without making the reverse contrast for each subject. For instance, let’s say you already have A-B for each subject and as a random effects analyses and now want to see the random effects result of B-A. Press results and select the SPM.mat file for the A-B random effects. Define new contrast, name it B-A, and put -1 in the vector window. This will give you the same result as if you had made a B-A contrast for each subject and then did a random effects analysis.
Simple Regression
Use this analysis when you have one number for each subject that you would like to regress against (i.e. age, perceived intensity of a taste). This should be a continuous variable. Note that if you have one number for each TRIAL (i.e. many numbers per subject) you should build a design matrix using a regressor (see parametric modulation on page 2). In this analysis you select one contrast image from each subject and look for activation present in that contrast that is correlated with your variable of interest. You can look for positive or negative correlation.
1. Optional: Load in your variable into the Matlab window if you have it saved as a variable (similar to loading variables in when making a design matrix).
2. Basic Models -> Simple Regression (correlation)
3. Select the con_XX.img files for each subject that you want to include in your analysis, where XX is the contrast number of your contrast of interest for each subject. After you have selected all the images, click Done.
4. [X] covariate: Here X is the number of images you have selected. Thus you must put in a vector of numbers that is X long, one number for each image. You can type these numbers in by hand or type in the name of the variable you loaded in step 1.
5. covariate name - type in the name of your variable
6. Grand mean scaling - no
7. Threshold masking - none
8. Explicitly mask images - no
9. Global calculation - omit
10. An SPM.mat file will be created showing the matrix you just made.
11. Press Estimate and select this SPM.mat file, then click Done.
12. When it is done estimating, press Results and select your SPM.mat file
13. Define new contrast
- name it (e.g. Contrast name (+1) with variable, or (-1) if you'll be doing a negative correlation
- In the vector area, type 1 for a positive correlation, -1 for a negative
14. Continue just as with other contrasts...
One-Way ANOVA and Masking
Use this to compare contrasts to each other (e.g. the main effect of A across subjects – the main effect of B across subjects) and to do inclusive or exclusive masking of contrasts across subjects. A inclusively masked by B will give you only activation from A that was also present in B. A exclusively masked by B will give you only activation from A that was not present in B. Note that A exclusively masked by B is not equivalent to B exclusively masked by A.
You can also use these results to create nice bar graphs that show group parameter estimates for each of your conditions if you specify each condition main effect as a group.
1. Make a directory for that particular contrast and cd into it.
2. Basic Models -> One-way Anova
3. #groups - Enter the number of contrasts you want to compare in this analysis (NOT the number of subject images). If you wanted to look at A masked by both B and C, you would have 3 groups.
4. Enter your Group 1 images. Select the con_XX.img files for each subject that you want to include in your analysis, where XX is the contrast number of your Group 1 contrast for each subject. After you have selected all the images in one group, click Done. If you still have remaining groups, you will be prompted to enter more images. Although I don’t think the order in which the contrast images are entered has to be identical in each group, it is a good idea to keep them constant.
5. Grand mean scaling - no
6. Threshold masking - none
7. Explicitly mask images - no
8. Global calculation - omit
9. non-spherecity correction? - The FIL spm2 website says “Sphericity refers to the assumption of identically and independently distributed errors.” This is what I think this means: if, for instance the contrasts you are comparing are drawn from the same set of image data (e.g. contrast A = sugar water and contrast B = (sugar water – baseline), then the errors for the two groups are not independent and you need non-spherecity correction. If your contrasts are completely separate from each other, you can select no. But you should definitely ask someone who actually knows statistics (i.e. not me) what to put for your particular study.
10. An SPM.mat file will be created showing the matrix you just made. There should be one column for each group.
11. Press Estimate and select this SPM.mat file, then click Done.
12. When it is done estimating, press Results and select your SPM.mat file
13. Define a contrast for each group
– Define new contrast
– name it (e.g. Group 1 contrast name )
– in the vector area, if you want to do masking, create the main effects of each group by typing in “1” for the column that represents that group and “0s” for other columns. (e.g. for group 2, type 0 1). If you want to do a subtraction (e.g. Group 1 – Group 2), type in the appropriate specification (e.g. 1 -1).
14. For subtraction contrasts, proceed as usual.
15. For masking (A masked by B)
– select your first contrast (A) and hit done.
– mask with other contrasts? – yes
– select the contrast(s) you want to mask by - done
– choose exclusive or inclusive masking. See intro to this section for explanations.
– continue as usual…
The SPM mailing list can be found at [[http://www.jiscmail.ac.uk/lists/spm.html|http://www.jiscmail.ac.uk/lists/spm.html]]
This site contains many quotes from that list. When a series of exchanges are followed on a topic they are in reverse chronological order. -drg
!Analyzing 3T Trio fMRI data with SPM2: the Ultimate Guide
Based on version 2.12 by EM, last revised 7/27/05
16/08/06 12:49 put on web but not updated (VeryVeryPreliminary)
@@1/09/06 10:37: Not completely formatted yet. Should be soon. @@
#PrePreProcessing
##[[Copying|PrePreProcessing/Copying]]
##[[Conversion|PrePreProcessing/Conversion]]
##[[Sorting|PrePreProcessing/Sorting]]
The commentary below illustrates how to setup contrasts on within subjects designs in SPM. This elegant explanation is from Jan Gläscher.
A PDF file with additional details can be found [[here|http://d.gitelman.googlepages.com/conweights.pdf]]
The basic design is a: 2x3 ANOVA with factor A (group, 2 levels), and factor B (condition, 3 levels).
In all the designs, 3 factors have been included
Factor 1: subject
Factor 2: group
Factor 3: condition
However, not all factors may be visible in the design matrix depending on which have been explicitly modelled.
In all the contrast weights below,
n1 = number of subjects in group1 (6)
n2 = number of subjects in group2 (5)
nc = number of conditions (=3)
----
''DESIGN 1''
<html>
<img src="http://d.gitelman.googlepages.com/dm1.gif" width="500">
</html>
included in design matrix are:
* main effect of subject (factor 1)
* group x condition interaction (factors 2 and 3)
contrast weights:
* ME group: ones(1,n1)*[nc/n1] -ones(1,n2)*[nc/n2] ones(1,nc) -ones(1,nc)
* ME cond: zeros(1,n1+n2) 1 0 -1 1 0 -1
* INT group x cond: zeros(1,n1+n2) 1 0 -1 -1 0 1
----
''DESIGN 2''
<html>
<img src="http://d.gitelman.googlepages.com/dm2.gif" width="500">
</html>
included in design matrix are:
* main effect of group
* main effect of condition
* group x condition interaction (factors 2 and 3)
contrast weights:
* ME group: 1 -1 zeros(1,nc) ones(1,nc)/nc -ones(1,nc)/nc
* ME cond: 0 0 1 0 -1 [1 0 -1]*[n1/(n1+n2)] [1 0 -1]*[n2/(n1+n2)]
* INT group x cond: 0 0 0 0 0 1 0 -1 -1 0 1
----
''DESIGN 3''
<html>
<img src="http://d.gitelman.googlepages.com/dm3.gif" width="500">
</html>
included in design matrix are:
* main effect of subject
* main effect of group
* main effect of condition
* group x condition interaction
contrast weights:
* ME group: ones(1,n1)/n1 -ones(1,n2)/n2 1 -1 0 0 0 ones(1,nc)/nc -ones(1,nc)/nc
* ME cond: zeros(1,n1+n2) 0 0 1 0 -1 [1 0 -1]*[n1/(n1+n2)] [1 0 -1]*[n2/(n1+n2)]
* INT group x cond: zeros(1,n1+n2) 0 0 zeros(1,nc) 1 0 -1 -1 0 1
Users of [[SPM|http://www.fil.ion.ucl.ac.uk/spm]] software
Would AFNI users be AFNIites or AFNers?
Would FSL users be FSLers (fisslers?) or FSLites (fisselites?)
Commentary on using SVC and VBM voxel statistics are below
John Ashburner (2002-09-01)
<<<
The extent statistic [//for VBM//] is definitely a problem because of the non-stationary smoothness of the residuals. Smoother regions are likely to produce bigger blobs. I would imagine that this non-stationarity would also have negative consequences for the validity of SVC for VBM data as it has been implemented in SPM99. Less smooth regions contain more resolution elements than average, so more independent t-tests are done and there is more chance of getting a false positive result (in these less smooth regions).
<<<
@@color:red;DRG: The same problem with using an extent threshold for VBM applies to both SPM2 and SPM5 version. However, one can use a VBM extent statistic with [[SNPM|http://www.sph.umich.edu/ni-stat/SnPM/]] or within the [[fmristat software|http://www.math.mcgill.ca/keith/fmristat/]] from Keith Worsley.@@
----
Karl Friston (2002-09-01)
Regarding the use of SVC for VBM
<<<
John is right in the sense that SPM99 uses a volume measure that assumes the smoothness is stationary, The 'statistical' volume is resel (resolution elements) per voxel times the number of voxels. In SPM99 the average resels per voxel over the search volume is used If the Small Volume in the SVC is small it may under-estimate or over-estimate the true statistical volume, if the local smoothnes is less than or more than the average. @@color:red;[DRG: The Resels Per Voxel value is saved in the RPV image but not used explicitly in any version of SPM].@@ This is not a problem unless there is substantial nonstationariness in the smoothness. __For VBM this may be the case. __
The way to fix this would be to use the resels per voxel estimate in the small volume (from the RPV.img). However, SPM99 does not use a local estimate. The simplest thing to do is to qualify your inference by making the stationariness assumption explicit and say the inference are only valid if the averge smoothness in the small volume is roughly the same as over the entire search volume. This can only be assured if the Gray matter partitions have been smoothed sufficiently (e.g. 8-12mm FHWM). Note that these comments apply to the p value based on height (not spatial extent).
<<<
@@color:red;DRG: Unfortunately the above statement generated confusion 3 years later in a series of exchanges on the list, i.e., what did it mean to qualify one's inference. Several further ways of dealing with this were then discussed above.@@
----
Karl Friston (2003-09-15)
//Regarding significant threshold for VBM {{{"}}}Can a threshold of 0.005 uncorrected be used?{{{"}}}//
<<<
I would use p <0.05, corrected for the search volume. This volume does not have to be the whole brain. The resulting correction can be controlled by specifying a restricted search volume using the SVC option. In some instances the corrected threshold at p =0.05, for a relatively small brain volume, will be much smaller than p =0.005 (uncorrected). Using the SVC involves motivating a more anatomically constrained search using your prior expectations or hypotheses.
<<<
@@color:red;DRG: This statement should be considered in light of previous statements above. The volume for SVC might need to be reasonably large in order for the average RPV for the search volume to be the same as for the whole brain.@@
----
Satoru Hayasaka (2005-02-22)
<<<
Use of cluster-extent test has been discouraged in VBM data analyses since the early days of VBM because of non-startionarity associated with warping during the spatial normalization process. To get around this problem, you have to measure the image smoothness locally at each voxel, then you have to adjust cluster extent based on this local smoothness measure. SPM produces an image called RPV.img (RESEL Per Voxel image) during an analysis, which can be considered as the measure of local "roughness" and can be used to adjust the cluster extent. The problem with RPV is that it has a lot of variability in it, since it's calculated at each voxel and not pooled over all the voxels. When you do a statistical inference, this variability in RPV needs to be accounted for, and reduces the sensitivity of the test quite a bit.
If non-stationarity is an issue, then I think Keith Worsley's [[fmristat|http://www.math.mcgill.ca/keith/fmristat/]] package should be able to do this "non-stationarity correction". We tried to implement the non-stationary correction using a permutation test, but a typical VBM data set is just too big for our program.
If you are interested in more details, please see Hayasaka et al., Non-Stationary Cluster Size Inference with Random Field and Permutation Methods. NeuroImage 22: 676-687 (2004).
<<<
Followed up with a question by Matthew Brett:
//Do you have a view on the extent of the problem if you simply assume that the image is stationary, at 8mm, 12mm smoothing?//
----
Satoru Hayasaka
<<<
I happened to have a VBM data set, smoothed with 12mm. I took a look at the RPV image, and I can tell that my data set is not stationary. The smoothest spot has FWHM about 30mm and the least smooth spot has FWHM about 12mm. So I don't think a heavy smoothing induces stationarity, and simply assuming stationarity could lead to biased results in a cluster size test. The p-values are underestimated for clusters in smooth areas, and are overestimated for clusters in rough areas. You are more likely to detect clusters in smooth areas than the ones in rough areas.
__@@color:red;Voxel Height and SVC@@__
A voxel-height test, on the other hand, seems to be robust to non-stationarity. But if it is to be used in an SVC context, I suggest re-calculating the average smoothness in an ROI, just in case that the ROI happens to be in a smooth / rough spot.
<<<
----
@@color:red;DRG: Naturally, the question then arose of how to deal with the RPV information.@@
Elena Antonova (2005-02-23)
//How does one correct for smoothness in ROI? I was simply comparing the smoothness (number of resels per voxels in the search volume) in the ROI relative to the average smoothness of the whole image. Is there a statistical procedure in SPM99 that allows correcting for smoothness in ROI during SVC?//
Satoru Hayasaka
<<<
First you might want to find the smoothness within your ROI. If you have a binary mask for the ROI, then first you mask the RPV image and calculate the sum of all the RPV voxels in the ROI. This should give you the number of RESELs in your ROI. Then based on the RESELs, you can calculate FWHM as
{{{
FWHM = ( V / RESELS )^(1/3)
}}}
where V is the number of voxels in the ROI.
^^//drg note: this was a correction to the formula posted the day before//^^
Once the smoothness is found, then you can change the smoothness for your SVC. In SPM2, //[and SPM]//, this can be done by changing xSPM.FWHM variable with FWHM * [1 1 1] (FWHM should be in voxels). In SPM99, I think the name of the variable is VOL.FWHM.
<<<
<<search>> <<closeAll>> <<permaview>> <<newTiddler>> <<newJournal 'DD MMM YYYY'>> <<fontSize "font-size:">> <<saveChanges>> <<upload http://brainimaging.tiddlyspot.com/store.cgi index.html . . brainimaging>> <<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
<<tabs txtMainTab Timeline Timeline TabTimeline All 'All tiddlers' TabAll Tags 'All tags' TabTags More 'More lists' TabMore>>
Neurology Brain Imaging Laboratory
http://brainimaging.tiddlyspot.com/
/***
|''Name:''|SparklinePlugin|
|''Description:''|Sparklines macro|
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};
//--
//-- Sparklines
//--
config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
{
var data = [];
var min = 0;
var max = 0;
var v;
for(var t=0; t<params.length; t++) {
v = parseInt(params[t]);
if(v < min)
min = v;
if(v > max)
max = v;
data.push(v);
}
if(data.length < 1)
return;
var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
box.title = data.join(",");
var w = box.offsetWidth;
var h = box.offsetHeight;
box.style.paddingRight = (data.length * 2 - w) + "px";
box.style.position = "relative";
for(var d=0; d<data.length; d++) {
var tick = document.createElement("img");
tick.border = 0;
tick.className = "sparktick";
tick.style.position = "absolute";
tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
tick.style.left = d*2 + "px";
tick.style.width = "2px";
v = Math.floor(((data[d] - min)/(max-min)) * h);
tick.style.top = (h-v) + "px";
tick.style.height = v + "px";
box.appendChild(tick);
}
};
}
//}}}
DESIGN MATRIX
Create anal folder in subject directory (i.e. mkdir anal) and cd into it. Be sure your present working directory is the anal folder, otherwise all the analysis files that SPM creates will be saved somewhere else and things will get confusing. Place necessary vector files into anal folder. Start Matlab and spm2.
Specify the event or block onsets
Event and block onsets are specified as vectors containing the numerical value (in
scans or seconds) corresponding to the start of the event or block. If you have multiple conditions within your study, you will need multiple vectors (e.g. for 2 conditions: A and B, you need two vectors). These vectors can be specified in two ways:
(1) as two variables within one Matlab m-file (.m) document. For instance,
A = [Atime1 Atime2 Atime3...] B = [Btime1 Btime2 Btime3...].
(2) as two separate text (.txt) files, each containing a column of numbers
specifying the onsets. The two files should have unique names (e.g. Aonsets, Bonsets).
Variables or filenames must start with a letter! Matlab won't accept those starting
with a number.
1. Load necessary vectors into Matlab command window
- if vectors saved as variables inside filename.m, type filename
- if vectors saved as column of numbers inside regular text file named filename, type load filename . The column of numbers will be saved as a variable named filename.
- type who to double-check that vectors are properly loaded. There should be a variable for each unique condition.
2. fmri -> design
3. Interscan interval = TR
4. Scans per session = # volumes per functional run (don't forget that you have taken out the dummies!). You need to enter a vector whose length equals the number of sessions. For instance, 3 sessions of 100 scans each would be entered as 100 100 100
5. How to specify - select scans or seconds (must match the units of your vectors)
6. Are sessions replications - no (i.e. sessions are different)
7. Select basis set - hrf with time derivative (the time derivative will account for extra variance in case the onsets are off by a little). For a study with regressors (e.g. ratings of intensity perception), might select hrf
8. Model interactions - no
9. # conditions
10. name of condition
11. vector of onset - type in name of vector variable
12. Duration
- In event-related designs
- Taste duration = length of time that liquid is being perceived in subject’s mouth (i.e. stimulus time + rest time before swallowing – initial 1.5 sec delay for subject perception).
- Odor duration = odor duration set in olfactometer
- For “instantaneous” events or swallow/rinses, duration = 0 [this is not set in stone, might want to check with EM/Dana]
- In block designs, duration = block duration in seconds or block duration/TR in scans
13. Parametric modulation
- none if not regressing with another variable
- other if regressing (e.g. intensity rating)
a) # parameters
b) type in regressor name and vector
c) polynomial order = 1 (i.e. linear)
14. Repeat steps 9-13 for each condition
15. Other regressors – 0
A. If you want to include movement as a usr specified regressor
i. You need to load the rp.txt files from each directory where the realignment data is stored (e.g. ProtA_run1). You will need to do this before designing the matrix (e.g. when you are loading the onset vectors)
ii. protAmove = load(spm_get);
iii. this will bring up spm get and you will chose the rp.txt file for protA. Continue this process for all prots (e.g A through E), selecting the rp.txt file associated with each protocol.
iv. When you get to usr specified regressors enter 6
v. You enter 6 because there are 6 movement realignment parameters x, y, z, pitch, raw, yol
vi. When it asks for the file you will chose protAmove
vii. This should load all 6 movement realignment parameters.
16. Repeat steps 8-15 for each session
17. Creates SPM.mat file saved in your anal directory
Output graphs:
- can switch between conditions using square SPM2 window on left
- lower left = the hrf with time derivative basis set
- upper left = signal vs. time. A Fourier transform gives you... ->
**Important for high-pass filter**
- upper right = signal (power, spectral density) vs. frequency, i.e. how strong the response is at different frequencies. This should look about the same for subjects within the same study. The default vertical line is at 128 seconds, any signal within the gray area will be cut off by high-pass filter. You should use this graph to determine what filter you want to use (don't want to lose too much signal power). The high-pass filter is necessary to get rid of low frequency noise.
- For most event-related designs, 128 seconds should be fine.
- For block designs, it could be higher (e.g. 256 seconds).
Specify the data
1. fmri -> data
2. Select the SPM.mat file just created
3. Select the "swaf" files for each session separately (i.e. click done after selecting each session's files).
4. Remove global effects - scale. This normalizes the intensity of the signal so that comparisons across subjects can be made.
5. High pass filter - specify
- Use the high pass filter frequency you've determined for your study based on the graph output after specifying the fMRI design (see above). For most event-related designs, 128 seconds should be fine.
- You need to specify a vector that contains one number for each of the sessions: e.g. if you wanted to use a 256 second filter, type repmat(256,1,x) where x is the number of sessions or type in 256 x times (with a space in between each number).
6. Unlike spm99, there is no low pass filter option. That was included to deal with autocorrelations in the data related to the time series but now this is taken care of with AR(1) modeling (see next step).
7. AR(1) serial correlations (corrects for the fact that each scan in the time series is correlated by lowering degrees of freedom).
- Select AR(1) for repeated measures analyses or non-random effect analyses (e.g. single subject)
- Select none if you will be doing random effects level analyses. RE collapses away from the time domain and is across subjects. You cannot report single subject results without the AR(1) model because the significance will not have been estimated correctly since autocorrelations will be ignored.
8. This step updates and replaces the previously made SPM.mat file.
Output
Each condition will have two columns, the first for the hrf and the second for the hrf time derivative. Rows indicate the scans. Vector onsets should roughly show which scan is matched up to each condition in each session.
Estimate the matix you have designed
1. Click on Estimate in the SPM2 window
2. Select the SPM.mat file, then click done.
3. This process could take some time (several hours if it's a big matrix with a lot of sessions and scans).
4. SPM will solve the general linear model for each voxel (y = x + e, solving for b),
therefore will get beta images
5. There are 61 planes because normalized data has 61 slices
6. Main effect analysis asks: is different than 0 for a condition x?
7. Subtractions: are two (different conditions) different from each other?
8. F contrasts automatically done: They show any activation that comes from a specified column but we usually don't care about that, so we want to look at T contrasts
If you get this error “can’t open image file” it means that the image were probably moved at some point. In this case, do the following.
1. look to see where the files (swaft) actually are.
2. load SPM.mat in the matlab window.
3. type in SPM.xY.P - this will show you where SPM thinks the files are.
4. If the result from step 3 is different from where the actually are you will need to update the file paths in the spm.mat file.
5. To do this:
a. There are two locations where you need to do this
b. Type new = spm_get; Select all swaft files in the order that they are set up in the design matrix (general by protocol not run number – in dms lab) and click done.
c. Type SPM.xY.P = new; This assigns all the files to the specified path for the first location.
d. The second location you need to update is SPM.xY.VY(:).fname To do this you need to set up a loop. To do this:
e. Type For i = 1:x (where x = total number of files that you have. (this sets up the loop)
f. Type SPM.xY.VY(i).fname = new(i,:);
g. Type end (this ends the loop).
6. Check to make sure everything has changed as intended. To do this type SPM.xY.P and you should see your new file names. Also type in SPM.xY.VY(:).fname to check this location.
7. Save what you have done for the next time you load SPM.mat.
a. Type save SPM.mat SPM
If you know that you moved the files since the time that you created the design matrix, you should also do this!
/***
Place your custom CSS here
***/
/*{{{*/
#mainMenu {position:absolute; left:0; width:10em; text-align:left; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
/*}}}*/
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}
h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}
.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}
.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}
.tabSelected{color:[[ColorPalette::PrimaryDark]];
background:[[ColorPalette::TertiaryPale]];
border-left:1px solid [[ColorPalette::TertiaryLight]];
border-top:1px solid [[ColorPalette::TertiaryLight]];
border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}
#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}
.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}
.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}
.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}
.tiddler .defaultCommand {font-weight:bold;}
.shadow .title {color:[[ColorPalette::TertiaryDark]];}
.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}
.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}
.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}
.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}
.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}
.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}
.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}
.imageLink, #displayArea .imageLink {background:transparent;}
.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}
.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}
.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}
.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}
.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}
.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}
#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
a {text-decoration:none;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em;}
#contentWrapper .chkOptionInput {border:0;}
.externalLink {text-decoration:underline;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}
.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}
#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}
.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0em 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler {padding:1em 1em 0em 1em;}
.missing .viewer,.missing .title {font-style:italic;}
.title {font-size:1.6em; font-weight:bold;}
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation {padding:0.5em; margin:0.5em;}
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}
.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}
.sparkline {line-height:1em;}
.sparktick {outline:0;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
| tiddlyspot password:|<<option pasUploadPassword>>|
| site management:|<<upload http://brainimaging.tiddlyspot.com/store.cgi index.html . . brainimaging>>//(requires tiddlyspot password)//<<br>>[[control panel|http://brainimaging.tiddlyspot.com/controlpanel]], [[download (go offline)|http://brainimaging.tiddlyspot.com/download]]|
| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|
tiddlyspot password:
<<option pasUploadPassword>>
/***
Contains the stuff you need to use Tiddlyspot
Note you must also have UploadPlugin installed
***/
//{{{
// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'brainimaging';
// make it so you can by default see edit controls via http -> no (drg, 03/19/08)
config.options.chkHttpReadOnly = true;
//window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
// disable autosave in d3
if (window.location.protocol != "file:")
config.options.chkGTDLazyAutoSave = false;
// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}
// create some shadow tiddler content
merge(config.shadowTiddlers,{
'WelcomeToTiddlyspot':[
"This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //What now?// @@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
"<<tiddler TspotControls>>",
"See also GettingStarted.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working online// @@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// @@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Help!// @@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// @@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),
'TspotControls':[
"| tiddlyspot password:|<<option pasUploadPassword>>|",
"| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<<br>>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
"| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),
'TspotSidebar':[
"<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),
'TspotOptions':[
"tiddlyspot password:",
"<<option pasUploadPassword>>",
""
].join("\n")
});
//}}}
<<upload http://brainimaging.tiddlyspot.com/store.cgi index.html . . brainimaging>><html><a href='http://brainimaging.tiddlyspot.com/download' class='button'>download</a></html>
The unwrapSlab code allows slabs of slices from an image to be wrapped to the other side of the volume. Wrapping usually occurs in the phase-encode direction and results in artifacts as shown in figure 1 below. These artifacts can interfere with image registration or normalization.
[img[http://d.gitelman.googlepages.com/origT1_worldspace.jpg]]
Figure 1: Image in World Space (i.e., with all affine transformations applied) as would be normally seen using the SPM Display function. Note that the back of the skull has been wrapped to the front.
The idea behind unwrapping is to move the wrapped set of slices back where they belong.
1. The first step is to display the image in Voxel Space.
##In SPM click the Display button and choose the volume to be unwrapped.
##Choose Voxel space from the popup menu on the lower right of the Graphics figure, (see Figure 2).
##Decide which voxels/slices need to be shifted. This decision must be made in relation to the voxel space (not the world space) display. In the image shown in Figure 3, I will choose to move slices 226-240 in the X direction.
[img[http://d.gitelman.googlepages.com/voxelspace_menu.gif]]
Figure 2: Choose voxel space from the popup menu.
The figure below shows the wrapped volume displayed in voxel space.
[img[http://d.gitelman.googlepages.com/origT1_voxelspace.jpg]]
Figure 3: Image in Voxel Space. The various axes have been labeled. These axes are invariant to the image display, however, the image will change in orientation depending on the type of acquisition. This image was acquired sagittally. The voxel numbering is shown on the image.
2. Select Unwrap Slab from the TASKS menu in the upper right of the SPM Graphics figure. Figure 4 shows a portion of the Unwrap Slab job with all the nodes expanded.
[img[http://d.gitelman.googlepages.com/unwrap_in_batch_box.gif]]
Figure 4: Unwrap Slab task with all nodes expanded.
3. Fill in the various choices:
##Source image: The image you want to unwrap.
##Keep position of wrapped images: Choose yes to adjust the origin for the movement of the slices and no to keep the same origin. Figure 8 shows the effects of not adjust or adjusting the origin.
##First slice in slab: This is the number of the first slice you want to move. It must always be less than the last slice in the slab. I will choose 226 (note: this is in the X direction).
##Last slice in slab: This is the number of the last slice you want to move. It must always be greater than the first slice in the slab. I will choose 240 (note: this is in the X direction).
##Wrap direction (voxel space): This is a menu with choices X, Y and Z. In this case, the slab is wrapped in the X direction.
##Output filename prefix: The default is U.
This set of choices produces the result shown in Figure 5. On the left are orthogonal views of the original wrapped volume and on the right orthogonal views of the unwrapped volume. Note thought that there is a small gap between the wrapped slices and the back of the head.
[img[http://d.gitelman.googlepages.com/T1_corrWgap.jpg]]
Figure 5: Original and unwrapped volumes. Slices 226 - 240 were wrapped in the X direction. Note the small gap (arrow) between the wrapped slices and the brain.
[img[http://d.gitelman.googlepages.com/T1_corrWgap_closeup.jpg]]
Figure 6: Closeup of unwrapped volume showing gap.
The gap between the unwrapped slices and the brain is caused by there being an extra slice (240) of all 0's in the image. This is not part of the wrapped slices but may be an artifact of having chosen a Rectangular FOV.
In order to fix this, I can instead choose to wrap slices 226 - 239. This in a nutshell is the advantage of this code in that it lets one choose a particular set of slices rather than always wrapping from the edge of the image.
Figure 7 shows the result of running the Unwrap Slab task again but with slices 226 - 239.
[img[http://d.gitelman.googlepages.com/T1_corrWnogap_closeup.jpg]]
Figure 7: Closeup of unwrapped image showing no gap.
The effect of adjusting the origin is shown in Figure 8.
[img[http://d.gitelman.googlepages.com/T1_unwrap_orgcomp.jpg]]
Figure 8: Adjusting the origin. The original image is on the left, an unadjusted, unwrapped image in the middle, and an adjusted, unwrapped image on the right. Note that relative to the brain, the origin is in the same position in the original and rightmost images (although its absolute position has shifted.
A 4x4 transformation matrix can be updated for an image file using the spm_get_space function.
#Get the transformation matrix you want to apply.
##Make it yourself. See the [[4x4TransformationMatrix]] tiddler.
##Extract it from an image using the spm_get_space utility itself: {{{M=spm_get_space('image_filename.img')}}}.
##Extract it from an existing image using spm_vol: {{{v=spm_vol('filename_with_matrix.img');}}}. v.mat will contain the transformation matrix. {{{M=v.mat}}}.
#Apply it to an image: {{{spm_get_space('imagefile_wo_matrix.img',M)}}}. //This assumes the transformation matrix was in a variable called M. Note you could also do this on nifti files (files ending in .nii).//
The image file {{{imagefile_wo_matrix.img}}} willl now have the transformation matrix M applied to it directly. This matrix will not have been multiplied by any preexisting transformation matrix, but will substitute for it.
| 18/03/2008 06:13:39 | drg | [[brainimaging.html|file:///C:/Documents%20and%20Settings/drg/My%20Documents/TiddlyWiki/NBIL/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/cbmg/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/cbmg/index.html]] | . | failed |
| 28/11/2008 13:20:59 | drg | [[brainimaging.html|file:///C:/Documents%20and%20Settings/drg/My%20Documents/TiddlyWiki/brainimaging/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/brainimaging/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/brainimaging/index.html]] | . | failed |
| 28/11/2008 13:21:22 | drg | [[brainimaging.html|file:///C:/Documents%20and%20Settings/drg/My%20Documents/TiddlyWiki/brainimaging/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/brainimaging/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/brainimaging/index.html]] | . | failed |
| 10/11/2010 17:06:09 | drg | [[brainimaging.html|file:///C:/Users/drg/TiddlyWiki/brainimaging/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/brainimaging/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/brainimaging/index.html]] | . |
| 10/11/2010 17:13:19 | drg | [[brainimaging.html|file:///C:/Users/drg/TiddlyWiki/brainimaging/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/brainimaging/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/brainimaging/index.html]] | . |
| 10/11/2010 17:20:43 | drg | [[brainimaging.html|file:///C:/Users/drg/TiddlyWiki/brainimaging/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/index.html]] | . | ok |
| 10/11/2010 18:16:01 | drg | [[brainimaging.html|file:///C:/Users/drg/TiddlyWiki/brainimaging/brainimaging.html]] | [[store.cgi|http://brainimaging.tiddlyspot.com/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/index.html]] | . |
| 12/07/2011 17:39:00 | drg | [[/|http://brainimaging.tiddlyspot.com/]] | [[store.php|http://brainimaging.tiddlyspot.com/store.php]] | . | [[index.html | http://brainimaging.tiddlyspot.com/index.html]] | |
| 12/07/2011 17:40:44 | drg | [[/|http://brainimaging.tiddlyspot.com/]] | [[store.cgi|http://brainimaging.tiddlyspot.com/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/index.html]] | . | ok |
| 12/07/2011 17:41:21 | drg | [[/|http://brainimaging.tiddlyspot.com/]] | [[store.php|http://brainimaging.tiddlyspot.com/store.php]] | . | [[index.html | http://brainimaging.tiddlyspot.com/index.html]] | |
| 12/07/2011 17:42:23 | drg | [[/|http://brainimaging.tiddlyspot.com/]] | [[store.cgi|http://brainimaging.tiddlyspot.com/store.cgi]] | . | [[index.html | http://brainimaging.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
major: 4, minor: 1, revision: 3,
date: new Date("Feb 24, 2008"),
source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0'
};
//
// Environment
//
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false; // true to activate both in Plugin and UploadService
//
// Upload Macro
//
config.macros.upload = {
// default values
defaultBackupDir: '', //no backup
defaultStoreScript: "store.php",
defaultToFilename: "index.html",
defaultUploadDir: ".",
authenticateUser: true // UploadService Authenticate User
};
config.macros.upload.label = {
promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
promptParamMacro: "Save and Upload this TiddlyWiki in %0",
saveLabel: "save to web",
saveToDisk: "save to disk",
uploadLabel: "upload"
};
config.macros.upload.messages = {
noStoreUrl: "No store URL in parmeters or options",
usernameOrPasswordMissing: "Username or password missing"
};
config.macros.upload.handler = function(place,macroName,params) {
if (readOnly)
return;
var label;
if (document.location.toString().substr(0,4) == "http")
label = this.label.saveLabel;
else
label = this.label.uploadLabel;
var prompt;
if (params[0]) {
prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
} else {
prompt = this.label.promptOption;
}
createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};
config.macros.upload.action = function(params)
{
// for missing macro parameter set value from options
if (!params) params = {};
var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
var username = params[4] ? params[4] : config.options.txtUploadUserName;
var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
// for still missing parameter set default value
if ((!storeUrl) && (document.location.toString().substr(0,4) == "http"))
storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
if (storeUrl.substr(0,4) != "http")
storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
if (!toFilename)
toFilename = bidix.basename(window.location.toString());
if (!toFilename)
toFilename = config.macros.upload.defaultToFilename;
if (!uploadDir)
uploadDir = config.macros.upload.defaultUploadDir;
if (!backupDir)
backupDir = config.macros.upload.defaultBackupDir;
// report error if still missing
if (!storeUrl) {
alert(config.macros.upload.messages.noStoreUrl);
clearMessage();
return false;
}
if (config.macros.upload.authenticateUser && (!username || !password)) {
alert(config.macros.upload.messages.usernameOrPasswordMissing);
clearMessage();
return false;
}
bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
return false;
};
config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
{
if (!storeUrl)
return null;
var dest = bidix.dirname(storeUrl);
if (uploadDir && uploadDir != '.')
dest = dest + '/' + uploadDir;
dest = dest + '/' + toFilename;
return dest;
};
//
// uploadOptions Macro
//
config.macros.uploadOptions = {
handler: function(place,macroName,params) {
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue("listWrapper",listWrapper);
this.refreshOptions(listWrapper,false);
var uploadCaption;
if (document.location.toString().substr(0,4) == "http")
uploadCaption = config.macros.upload.label.saveLabel;
else
uploadCaption = config.macros.upload.label.uploadLabel;
wizard.setButtons([
{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
onClick: config.macros.upload.action},
{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
]);
},
options: [
"txtUploadUserName",
"pasUploadPassword",
"txtUploadStoreUrl",
"txtUploadDir",
"txtUploadFilename",
"txtUploadBackupDir",
"chkUploadLog",
"txtUploadLogMaxLine"
],
refreshOptions: function(listWrapper) {
var opts = [];
for(i=0; i<this.options.length; i++) {
var opt = {};
opts.push();
opt.option = "";
n = this.options[i];
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
opts.push(opt);
}
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n<opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h && h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
}
}
},
onCancel: function(e)
{
backstage.switchTab(null);
return false;
},
wizardTitle: "Upload with options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br>",
cancelButton: "Cancel",
cancelButtonPrompt: "Cancel prompt",
listViewTemplate: {
columns: [
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
};
//
// upload functions
//
if (!bidix.upload) bidix.upload = {};
if (!bidix.upload.messages) bidix.upload.messages = {
//from saving
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to upload backup file",
rssSaved: "RSS feed uploaded",
rssFailed: "Failed to upload RSS feed file",
emptySaved: "Empty template uploaded",
emptyFailed: "Failed to upload empty template file",
mainSaved: "Main TiddlyWiki file uploaded",
mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
//specific upload
loadOriginalHttpPostError: "Can't get original file",
aboutToSaveOnHttpPost: 'About to upload on %0 ...',
storePhpNotFound: "The store script '%0' was not found."
};
bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
var callback = function(status,uploadParams,original,url,xhr) {
if (!status) {
displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
return;
}
if (bidix.debugMode)
alert(original.substr(0,500)+"\n...");
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
bidix.upload.uploadRss(uploadParams,original,posDiv);
};
if(onlyIfDirty && !store.isDirty())
return;
clearMessage();
// save on localdisk ?
if (document.location.toString().substr(0,4) == "file") {
var path = document.location.toString();
var localPath = getLocalPath(path);
saveChanges();
}
// get original
var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
var originalPath = document.location.toString();
// If url is a directory : add index.html
if (originalPath.charAt(originalPath.length-1) == "/")
originalPath = originalPath + "index.html";
var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
var log = new bidix.UploadLog();
log.startUpload(storeUrl, dest, uploadDir, backupDir);
displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
if (bidix.debugMode)
alert("about to execute Http - GET on "+originalPath);
var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
bidix.upload.uploadRss = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
if(status) {
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
bidix.upload.uploadMain(params[0],params[1],params[2]);
} else {
displayMessage(bidix.upload.messages.rssFailed);
}
};
// do uploadRss
if(config.options.chkGenerateAnRssFeed) {
var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
var rssString = generateRss();
// no UnicodeToUTF8 conversion needed when location is "file" !!!
if (document.location.toString().substr(0,4) != "file")
rssString = convertUnicodeToUTF8(rssString);
bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
} else {
bidix.upload.uploadMain(uploadParams,original,posDiv);
}
};
bidix.upload.uploadMain = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
var log = new bidix.UploadLog();
if(status) {
// if backupDir specified
if ((params[3]) && (responseText.indexOf("backupfile:") > -1)) {
var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
}
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
store.setDirty(false);
log.endUpload("ok");
} else {
alert(bidix.upload.messages.mainFailed);
displayMessage(bidix.upload.messages.mainFailed);
log.endUpload("failed");
}
};
// do uploadMain
var revised = bidix.upload.updateOriginal(original,posDiv);
bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};
bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
var localCallback = function(status,params,responseText,url,xhr) {
url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
if (xhr.status == httpStatus.NotFound)
alert(bidix.upload.messages.storePhpNotFound.format([url]));
if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
alert(responseText);
if (responseText.indexOf("Debug mode") >= 0 )
responseText = responseText.substring(responseText.indexOf("\n\n")+2);
} else if (responseText.charAt(0) != '0')
alert(responseText);
if (responseText.charAt(0) != '0')
status = null;
callback(status,params,responseText,url,xhr);
};
// do httpUpload
var boundary = "---------------------------"+"AaB03x";
var uploadFormName = "UploadPlugin";
// compose headers data
var sheader = "";
sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
sheader += uploadFormName +"\"\r\n\r\n";
sheader += "backupDir="+uploadParams[3] +
";user=" + uploadParams[4] +
";password=" + uploadParams[5] +
";uploaddir=" + uploadParams[2];
if (bidix.debugMode)
sheader += ";debug=1";
sheader += ";;\r\n";
sheader += "\r\n" + "--" + boundary + "\r\n";
sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
sheader += "Content-Length: " + data.length + "\r\n\r\n";
// compose trailer data
var strailer = new String();
strailer = "\r\n--" + boundary + "--\r\n";
data = sheader + data + strailer;
if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
if (!posDiv)
posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
store.allTiddlersAsHtml() + "\n" +
original.substr(posDiv[1]);
var newSiteTitle = getPageTitle().htmlEncode();
revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
return revised;
};
//
// UploadLog
//
// config.options.chkUploadLog :
// false : no logging
// true : logging
// config.options.txtUploadLogMaxLine :
// -1 : no limit
// 0 : no Log lines but UploadLog is still in place
// n : the last n lines are only kept
// NaN : no limit (-1)
bidix.UploadLog = function() {
if (!config.options.chkUploadLog)
return; // this.tiddler = null
this.tiddler = store.getTiddler("UploadLog");
if (!this.tiddler) {
this.tiddler = new Tiddler();
this.tiddler.title = "UploadLog";
this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
this.tiddler.created = new Date();
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
}
return this;
};
bidix.UploadLog.prototype.addText = function(text) {
if (!this.tiddler)
return;
// retrieve maxLine when we need it
var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
if (isNaN(maxLine))
maxLine = -1;
// add text
if (maxLine != 0)
this.tiddler.text = this.tiddler.text + text;
// Trunck to maxLine
if (maxLine >= 0) {
var textArray = this.tiddler.text.split('\n');
if (textArray.length > maxLine + 1)
textArray.splice(1,textArray.length-1-maxLine);
this.tiddler.text = textArray.join('\n');
}
// update tiddler fields
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
// refresh and notifiy for immediate update
story.refreshTiddler(this.tiddler.title);
store.notify(this.tiddler.title, true);
};
bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {
if (!this.tiddler)
return;
var now = new Date();
var text = "\n| ";
var filename = bidix.basename(document.location.toString());
if (!filename) filename = '/';
text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
text += config.options.txtUserName + " | ";
text += "[["+filename+"|"+location + "]] |";
text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
text += uploadDir + " | ";
text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
text += backupDir + " |";
this.addText(text);
};
bidix.UploadLog.prototype.endUpload = function(status) {
if (!this.tiddler)
return;
this.addText(" "+status+" |");
};
//
// Utilities
//
bidix.checkPlugin = function(plugin, major, minor, revision) {
var ext = version.extensions[plugin];
if (!
(ext &&
((ext.major > major) ||
((ext.major == major) && (ext.minor > minor)) ||
((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
// write error in PluginManager
if (pluginInfo)
pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
}
};
bidix.dirname = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(0, lastpos);
} else {
return filePath.substring(0, filePath.lastIndexOf("\\"));
}
};
bidix.basename = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("#")) != -1)
filePath = filePath.substring(0, lastpos);
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(lastpos + 1);
} else
return filePath.substring(filePath.lastIndexOf("\\")+1);
};
bidix.initOption = function(name,value) {
if (!config.options[name])
config.options[name] = value;
};
//
// Initializations
//
// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);
// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");
//optionsDesc
merge(config.optionsDesc,{
txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
txtUploadUserName: "Upload Username",
pasUploadPassword: "Upload Password",
chkUploadLog: "do Logging in UploadLog (default: true)",
txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});
// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');
// Backstage
merge(config.tasks,{
uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");
//}}}
31/08/06
!VBM USING C. GASER'S VBM TOOLBOX
@@CHECK FILES AND CORRECT IMAGE WRAPPING@@
#Check all files for general goodness and appropriate voxel values
##Select files with check reg
##Right click one of the displayed voluems and choose image > window > global > manual
##Set the manual threshold to something like 0 200. In general T1 volumes should display appropriately at that threshold. If you get an image that looks all white, the voxel values are probably off.
##Also check if any image looks obviously corrupt.
##Replace any bad images.
#Reorient images so that the origin is roughly at the AC and the 0 axial plane roughly runs through AC-PC.
#Use the [[unwrapping]] tool to shift wrapped parts of the image to the correct place.
@@SKULL STRIP THE BRAINS USING BET2 FROM THE FSL SOFTWARE@@
##This is an optional step. It improves the normalization and segmentation results in normal brains, but may not help, or may worsen results, with abnormal brains. Because abnormal brains may be very distorted, keeping the skull in place may help to constrain the normlization.
##Download the [[extractbrain|http://d.gitelman.googlepages.com/extractbrain.zip]] toolbox, unzip it, and put it in your spm5/toolbox directory.
##Restart spm5 if it is already running, to make the toolbox appear under the Toolbox menu.
##More to follow about using the toolbox
@@USING THE VBM5 TOOLBOX@@
#Run the vbm5 toolbox from [[Christian Gaser|http://dbm.neuro.uni-jena.de/vbm]]
**The toolbox does an extraordinarily nice job of segmentation and normalization with the addition of the HMRF step.
##When using the toolbox choose to write out modulated+unmodulated gray, white and csf images.
##Also write out the bias corrected T1 image.
##None of the other defaults were changed. The standard template was used.
##The toolbox does not write out the standard normalized brain volumes (only the segmented ones) so you'll have to do this later.
##Choose the volumes.
##Save the Batch File.
##Run it.
Files that are written out:
|F400at1vol250.img |original volume |
|mF400at1vol250.img |bias corrected original volume |
|mwc1F400at1vol250_HMRF.img |modulated, normalized, gray matter segment |
|mwc2F400at1vol250_HMRF.img |modulated, normalized, white matter segment |
|mwc3F400at1vol250_HMRF.img |modulated, normalized, csf segment |
|wc1F400at1vol250_HMRF.img |unmodulated, normalized, gray matter segment |
|wc2F400at1vol250_HMRF.img |unmodulated, normalized, white matter segment |
|wc3F400at1vol250_HMRF.img |unmodulated, normalized, csf segment |
@@NOTE: The file naming corrections are a wee bit confusing (not Christian's fault). Prepending an "m" to the file name can either mean an image that is bias corrected, or one that has been modulated. In general, modulated images are going to be those that have been normalized (warped or w), so mw... images are those that have been modulated and warped.@@
The script also creates the parameter files both for warping the images into normalized space and for unwarping them.
These files were obtained by further steps: smoothing and writing normalized respectively.
|smwc1F400at1vol250_HMRF.img |smoothed, modulated, normalized, gray matter segment |
|wmF400at1vol250.img |normalized, bias corrected volume |
@@WRITING NORMALIZED T1 VOLUMES@@
#Run a normalization batch script, choosing write normalize as the procedure.
#Select the appropriate parameter files and images to write as normalized.
##Since you now have bias corrected images, it makes sense to use those.
@@SMOOTHING@@
#Run a smoothing batch script.
#Change the default FWHM to [12 12 12]
#Choose the mwc1... images.
#The procedure will produce smwc1... images.
@@MAKING A MASK@@
#I found that if the VBM analysis was run without an explicit masking volume there were all sorts of non-significant but troubling differences found outside the brain. Presumably these relate to very low value voxels that nevertheless end up as different between the groups. For this reason a mask was created.
#Run an Image Calculator procedure.
##Choose all the unsmoothed gray + white + csf segments from all the subjects
##Give the output file a name.
##The equation is (i1+i2+i3+...+in)/3/num_of_subjs. This essentially gives an average image across all subjects of the intracranial contents.
#Threshold this image at 0.001 using another image calculator. The equation is (i1.*(i1>0.001))
#Display the image using check reg.
#Right click the image and start the ROI toolbox.
##Select the image as its own ROI.
##Set an erode/dilate threshold of 0.1 and erode/dilate the image. This should get rid of clusters outside the brain.
##Save the image. (One of the menu choices in the ROI tools.)
#Smooth the image with a 2mm ROI.
#Threshold it again at 0.05.
#This produced a brain mask that is almost the size of the average brain (it's just a bit bigger) and it has no voxels hanging out in the background.
@@CALCULATING INTRACRANIAL VOLUME@@
#The VBM toolbox nicely save for each image a text file that contains the volume in mm^3 of the image segments that were written out. (You did remember to write out gray + white + csf, didn't you?)
#Use the [[getVBMvol]] function. @@nlvol = getVBMvol;@@
##The function has you select the text files containing the volume information.
##It outputs a variable organized as a structure with the fields listed in the table below.
##Save this variable for later use.
|subjid |The filename containing the volume data |
|gvol |gray matter volume|
|wvol|white matter volume|
|cvol|csf volume|
|tvol|total intracranial volume (the sum of gvol+wvol+cvol|
@@SETTING UP THE DESIGN@@
#Select a two-sample t-test design.
#Enter the scans for each group.
#Independence = yes.
#Variance = Unequal
#Grand Mean Scaling = no
#ANCOVA = no
#Covariates
##Enter a single covariate consisting of the Total Intracranial Volumes of each of the subject. Make sure it is entered in the exact same order as the scans.
##Name = TIV
##Interactions = None
##Centering = Overall mean
#Threshold masking = none
#Implicit mask = yes
#Explicit mask = choose the mask you made above.
#Global calculation = omit
#Global normalization = no
#Normalization = None
#Directory = choose a directory for the analysis.
@@ESTIMATE THE DESIGN@@
@@LOOK AT THE RESULTS@@
hope this helps,
drg
Not finished
May contain many mistakes (well that would be true anyways)
<!--{{{-->
<div macro="showWhen readOnly"><div class='toolbar'> <span style="padding-right:8.75em;" macro='dropTags "current tags: "+config.macros.dropTags.dropdownchar}}'></span> <span macro='toolbar -closeTiddler closeOthers permalink references jump fullscreen'></span></div></div>
<div macro="hideWhen readOnly"><div class='toolbar'> <span style="padding-right:8.75em;" macro='dropTags "current tags: "+config.macros.dropTags.dropdownchar}}'></span> <span macro='toolbar -closeTiddler closeOthers +editTiddler permalink references jump fullscreen'></span> </div></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.
@@font-weight:bold;font-size:1.3em;color:#444; //What now?// @@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://brainimaging.tiddlyspot.com/controlpanel]] (your control panel username is //brainimaging//).
<<tiddler TspotControls>>
See also GettingStarted.
@@font-weight:bold;font-size:1.3em;color:#444; //Working online// @@ You can edit this ~TiddlyWiki right now, and save your changes using the "save to web" button in the column on the right.
@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// @@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click "upload" and your ~TiddlyWiki will be saved back to tiddlyspot.com.
@@font-weight:bold;font-size:1.3em;color:#444; //Help!// @@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].
@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// @@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions.
... still working on this ...
so not even close to being ready.
% code from mars_build_roi;
[Finter,Fgraph,CmdLine] = spm('FnUIsetup','Build ROI', 0);
% get ROI type
optfields = {'image','voxel', 'sphere', 'box_cw', 'box_lims'};
optlabs = {'Image', 'Voxel', 'Sphere',...
'Box (centre,widths)', 'Box (ranges XYZ)'};
roitype = char(...
spm_input('Type of ROI', '+1', 'm',{optlabs{:} 'Quit'},...
{optfields{:} 'quit'},length(optfields)+1));
img_flt = 'image';
c = spm_input('Centre of sphere (mm)', '+1', 'e', [], 3);
r = spm_input('Sphere radius (mm)', '+1', 'r', 10, 1);
d = sprintf('%0.1fmm radius sphere at [%0.1f %0.1f %0.1f]',r,c);
l = sprintf('sphere_%0.0f-%0.0f_%0.0f_%0.0f',r,c);
o = maroi_sphere(struct('centre',c,'radius',r));
params = struct('centre', c,...
'radius, r);
maroi_sphere(params)
/***
|''Name:''|''dropTags''|
|''Version:''|0.5 (12-May-2006)|
|''Created by:''|SaqImtiaz|
|''Location:''|http://tw.lewcid.org/#DropTagsMacro|
|''Description:''|provides a drop down list of tags in the current tiddler,<<br>> a replacement for the core tags macro.|
|''Documentation:''|DropTagsMacroDocumentation |
|''Source Code:''|DropTagsMacroSource |
|''Requires:''|~TW2.07|
!About
*provides a drop down list of tags in the current tiddler, a replacement for the core tags macro.
''I recommend using either TaggerPlugin or monkeyTagger, with dropTags and dropTagging in the toolbar:''
!Usage
{{{<<dropTags>>}}} for <<dropTags>>
or {{{<<dropTags 'custom label'>>}}} for <<dropTags 'custom label'>>
!Installation:
*Copy this tiddler to your TW with the systemConfig tag
* copy the following to your ViewTemplate:
#either {{{<div class='tagged' macro='dropTags'></div>}}} to add to next to the tags macro in the viewer area, or
#{{{<div class='toolbar' >
<span style="padding-right:8.75em;" macro='dropTags "current tags: "+config.macros.dropTags.dropdownchar}}'></span>
<span macro='toolbar -closeTiddler closeOthers +editTiddler permalink references jump'></span>
</div>}}}
!History
*May 12th, version 0.5, fixed some nesting bugs, added support for IntellitaggerPlugin.
*May 3rd, version 0.41, made compatible with CustomPopups.
!Source Code
***/
//{{{
config.macros.dropTags={};
config.macros.dropTags.dropdownchar = (document.all?"▼":"▾"); // the fat one is the only one that works in IE
config.macros.dropTags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var arrow=config.macros.dropTags.dropdownchar;
var droptaglabel= (params[0] && params[0] !='.')? params[0]+arrow: 'tags'+arrow;
var droptagtooltip="current tags for this tiddler";
var droptag = function(e){
if (!e) var e = window.event;
var popup = Popup.create(this);
var lingo = config.views.wikified.tag;
if (tiddler.tags.length==0)
createTiddlyElement(popup,"li",null,"listTitle",lingo.labelNoTags);
else
for(var t=0; t<tiddler.tags.length; t++)
{createTagButton(createTiddlyElement(popup,"li"),tiddler.tags[t],tiddler.title);}
if (version.extensions.IntelliTaggerPlugin)
{createTiddlyElement(createTiddlyElement(popup,"li"),"hr");
abego.IntelliTagger.createEditTagsButton(tiddler, createTiddlyElement(popup,"li"),"[IntelliEdit]","Edit tags with Intellitagger");}
Popup.show(popup,false);
e.cancelBubble = true;
if (e.stopPropagation)
e.stopPropagation();
return(false);
};
createTiddlyButton(place,droptaglabel,droptaglabel,droptag,"button","dropTagBtn");
};
setStylesheet(
".popup .highlight{background: #fe8; color:#000;}\n"+
"#nestedtagger {background:#2E5ADF; border: 1px solid #0331BF;}\n"+
"",
"DropTagsStyles");
if (!config.macros.tagger)
window.onClickTag=function(e){
if (!e) var e = window.event;
var theTarget = resolveTarget(e);
var nested = (!isNested(theTarget));
if ((Popup.stack.length > 1)&&(nested==true)) {Popup.removeFrom(1);}
else if(Popup.stack.length > 0 && nested==false) {Popup.removeFrom(0);};
var theId = (nested==false)? "popup" : "nestedtagger";
var popup = createTiddlyElement(document.body,"ol",theId,"popup",null);
Popup.stack.push({root: this, popup: popup});
var tag = this.getAttribute("tag");
var title = this.getAttribute("tiddler");
if(popup && tag)
{
var tagged = store.getTaggedTiddlers(tag);
var titles = [];
var li,r;
for(r=0;r<tagged.length;r++)
if(tagged[r].title != title)
titles.push(tagged[r].title);
var lingo = config.views.wikified.tag;
if(titles.length > 0)
{
var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
openAll.setAttribute("tag",tag);
createTiddlyElement(createTiddlyElement(popup,"li"),"hr");
for(r=0; r<titles.length; r++)
{
createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
}
}
else
createTiddlyText(createTiddlyElement(popup,"li",null,"disabled"),lingo.popupNone.format([tag]));
createTiddlyElement(createTiddlyElement(popup,"li"),"hr");
var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
createTiddlyText(h,lingo.openTag.format([tag]));
}
Popup.show(popup,false);
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
return(false);
}
if (!window.isNested)
window.isNested = function(e) {
while (e != null) {
var contentWrapper = document.getElementById("contentWrapper");
if (contentWrapper == e) return true;
e = e.parentNode;
}
return false;
};
config.shadowTiddlers.DropTagsMacroDocumentation="The documentation is available [[here.|http://tw.lewcid.org/#DropTagsMacroDocumentation]]";
config.shadowTiddlers.DropTagsMacroSource="The documentation is available [[here.|http://tw.lewcid.org/#DropTagsMacroDocumentation]]";
//}}}
Changes the [[endianess|http://en.wikipedia.org/wiki/Endian]] specification of images referenced in an SPM.mat file. Only works on, and is only needed by, SPM2 and SPM99 SPM.mat files that have been moved between machines of different endianess. 27/09/06 19:51
{{{
function SPMout = endianchanger(SPM)
% ENDIANCHANGER change the endian datatype for an spm2 analysis
% ENDIANCHANGER(SPM) changes the datatype specifications in an SPM2
% generated SPM.mat file. The program assumes that all image volumes
% specified in the SPM.mat file are byte swapped, and changes them
% accordingly.
%
% SPM2 does not specify an absolute endianess for the data. Data are
% either byte-swapped or not byte-swapped relative to how they are
% written to disk. SPM5 does specify the endianess.
%
% Volume fields include: SPM.xY.VY, SPM.xM.VM, SPM.Vbeta, SPM.VResMS,
% SPM.VM, SPM.xCon.Vcon, SPM.xCon.Vspm.
%
% The function returns the modified SPM structure. This must be saved by
% the user.
% 28/09/06 09:15 bug fix by Christian Matthiae on checking for SPM99
% SPM.mat files, and for selection of an SPM.mat file using uigetfile.
% Author: Darren Gitelman
% $Id: endianchanger.m,v 1.1 2006-09-28 drg Exp drg $
SPMout = [];
if nargin == 0
[p di]=uigetfile('*.mat','Select an SPM.mat file for byte-swapping');
load(fullfile(di,p));
end
if ~strcmp(SPM.SPMid(1:4),'SPM2') && ~strcmp(SPM.SPMid(1:5),'SPM99')
error('This program only works on SPM99 and SPM2 data.')
end
% Process each field with volume information
fprintf('Swapping:\n')
% SPM.xY.VY
if ~isempty(SPM.xY.VY)
fprintf('SPM.xY.VY\n');
for i = 1:numel(SPM.xY.VY)
% check which datatype to pick. This is most assuredly excessive to
% check on each volume, but the time cost is small.
dt = SPM.xY.VY(i).dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.xY.VY(i).dim(4) = dt;
SPM.xY.VY(i).private.hdr.dime.datatype = dt;
end
end
% SPM.xM.VM
if ~isempty(SPM.xM.VM)
fprintf('SPM.xM.VM\n');
dt = SPM.xM.VM.dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.xM.VM.dim(4) = dt;
SPM.xM.VM.private.hdr.dime.datatype = dt;
end
% SPM.Vbeta
if ~isempty(SPM.Vbeta)
fprintf('SPM.Vbeta\n');
for i = 1:numel(SPM.Vbeta)
% check which datatype to pick. This is most assuredly excessive to
% check on each volume, but the time cost is small.
dt = SPM.Vbeta(i).dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.Vbeta(i).dim(4) = dt;
SPM.Vbeta(i).private.hdr.dime.datatype = dt;
end
end
% SPM.VResMS
if ~isempty(SPM.VResMS)
fprintf('SPM.VResMS\n')
for i = 1:numel(SPM.VResMS)
% check which datatype to pick. This is most assuredly excessive to
% check on each volume, but the time cost is small.
dt = SPM.VResMS(i).dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.VResMS(i).dim(4) = dt;
SPM.VResMS(i).private.hdr.dime.datatype = dt;
end
end
% SPM.VM
if ~isempty(SPM.VM)
fprintf('SPM.VM\n')
dt = SPM.VM.dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.VM.dim(4) = dt;
SPM.VM.private.hdr.dime.datatype = dt;
end
% SPM.xCon.Vcon & SPM.xCon.Vspm
fprintf('SPM.xCon.Vcon\n')
fprintf('SPM.xCon.VSPM\n')
for i = 1:numel(SPM.xCon)
% check which datatype to pick. This is most assuredly excessive to
% check on each volume, but the time cost is small.
if ~isempty(SPM.xCon(i).Vcon)
dt = SPM.xCon(i).Vcon.dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.xCon(i).Vcon.dim(4) = dt;
SPM.xCon(i).Vcon.private.hdr.dime.datatype = dt;
end
if ~isempty(SPM.xCon(i).Vspm)
dt = SPM.xCon(i).Vspm.dim(4);
if dt < 512
dt = dt*256;
else
dt = dt/256;
end
SPM.xCon(i).Vspm.dim(4) = dt;
SPM.xCon(i).Vspm.private.hdr.dime.datatype = dt;
end
end
SPMout = SPM;
fprintf('Done\n')
}}}
This script allows extracting (skull stripping) the brain from within SPM5. In order to use it you must have BET2 from the [[FSL software|http://www.fmrib.ox.ac.uk/fsl/]] (version 3.3 or greater).
The function should be installed in a directory called ''//extractbrain//'' in the spm5 / toolbox directory. It will appear under the Tools submenu and can be invoked serially or in a batch script.
To use: select the name of the brain(s) to extract and the path to ~BET2.
There are various other choices for the estimation parameters and the types of images to generate. These are explained in the help notes,
----
{{{
function opts = cbmg_config_extractbrain
% CBMG_CONFIG_EXTRACTBRAIN configuration file for brain extraction using bet2
% OPTS = CBMG_CONFIG_EXTRACTBRAIN returns an options structure for task
% batching in SPM5.
%
% BET2 from the FSL software version >= 3.3 is required for use. BET2 and
% FSL are copyright 2006, The University of Oxford.
% Author: Darren Gitelman
% $Id: cbmg_config_extractbrain.m,v 1.2 2006-11-25 17:40:01-06 drg Exp drg $
addpath(fullfile(spm('dir'),'toolbox','extractbrain'));
%_______________________________________________________________________
entry = inline(['struct(''type'',''entry'',''name'',name,'...
'''tag'',tag,''strtype'',strtype,''num'',num)'],...
'name','tag','strtype','num');
files = inline(['struct(''type'',''files'',''name'',name,'...
'''tag'',tag,''filter'',fltr,''num'',num)'],...
'name','tag','fltr','num');
mnu = inline(['struct(''type'',''menu'',''name'',name,'...
'''tag'',tag,''labels'',{labels},''values'',{values})'],...
'name','tag','labels','values');
branch = inline(['struct(''type'',''branch'',''name'',name,'...
'''tag'',tag,''val'',{val})'],...
'name','tag','val');
repeat = inline(['struct(''type'',''repeat'',''name'',name,''tag'',tag,'...
'''values'',{values})'],'name','tag','values');
%_______________________________________________________________________
source = files('Brains to extract','brains','image',[1 Inf]);
source.help = 'Select the brain(s) to extract.';
% Extraction Options
% -------------------------------------------------------------------
bet2path = files('Path to bet2','bet2path','*',[1 1]);
bet2path.ufilter = '.*^(bet|BET)2';
bet2path.help = {'Choose the bet2 executable file.'};
fracthresh = entry('Fractional threshold','fracthr','e',[1 1]);
fracthresh.val = {0.5};
fracthresh.extras = [0 1];
fracthresh.help = {[...
'[-f <f>] Fractional intensity threshold. Range: [0 1]. '...
'Default = 0.5. Smaller values give larger brain outline estimates.']};
vertgrad = entry('Vertical gradient','vertgrad','e',[1 1]);
vertgrad.val = {0};
vertgrad.extras = [-1 1];
vertgrad.help = {[...
'[-g <g>] Vertical gradient in fractional intensity threshold. '...
'Range: [-1 1]. Default = 0. Positive values give larger brain '...
'outline at bottom, smaller at top.']};
headrad = entry('Head radius','headrad','e',[1 1]);
headrad.val = {-1};
headrad.help = {[...
'[-r,--radius <r>]. OPTIONAL. Head radius in mm not voxels! '...
'Default: <None>. Initial surface sphere is set to half of this. '...
'A value of [-1] means <None>.']};
cog = entry('Centre-of-gravity','cog','e',[1 3]);
cog.val = {[-1 -1 -1]};
cog.help = {[...
'[-c <x y z>]. OPTIONAL. Centre-of-gravity, in mm not voxels, '...
'of initial mesh surface. Default: <None>. A value of [-1 -1 -1] '...
'means <None>.']};
thrsh = mnu('Thresholding','thrsh',{'No','Yes'},{[],' -t'});
thrsh.val = {[]};
thrsh.help = {[...
'[-t, --threshold]. Apply thresholding to skull stripped brain image '...
'and mask. Default: <None>.']};
verbose = mnu('Verbose','verbose',{'No','Yes'},{[],' -v'});
verbose.val = {' -v'};
verbose.help = {[...
'[-v,--verbose]. Print diagnostic messages on Matlab command line. '...
'Default: Yes.']};
% WRITE OPTIONS
% -------------------------------------------------------------------
nooutput = mnu('Make skull stripped brain','noout',{'No','Yes'},{' -n',[]});
nooutput.val = {[]};
nooutput.help = {[...
'[-n, --nooutput]. Generate skull stripped brain image. Default: Yes.']};
outline = mnu('Make brain surface outline','outln',{'No','Yes'},{[],' -o'});
outline.val = {[]};
outline.help = {[...
'[-o,--outline]. Generate brain surface outline overlaid onto'...
'original image. Default: No.']};
mask = mnu('Make binary brain mask.','mask',{'No','Yes'},{[],' -m'});
mask.val = {[]};
mask.help = {'[ -m,--mask]. Generate binary brain mask. Default: No.'};
skull = mnu('Make skull image.','skull',{'No','Yes'},{[],' -s'});
skull.val = {[]};
skull.help = {'[-s,--skull]. Generate approximate skull image. Default: No.'};
surfoff = mnu('Make brain surface.','surf',{'No','Yes'},{[],' -e'});
surfoff.val = {[]};
surfoff.help = {'[-e, --mesh]. Generate brain surface as mesh in off format.'};
fsloutput = mnu('FSL output image type','fslout',{'ANALYZE','NIFTI'},{'ANALYZE','NIFTI'});
fsloutput.val = {'ANALYZE'};
fsloutput.help = {[...
'Ouput type for writing images. Although FSL can write ANALYZE, NIFTI '...
'NIFTI_PAIR, ANALYZE_GZ, NIFTI_GZ and NIFTI_PAIR_GZ, SPM only '...
'implements the first two.']};
% Main Configuration Branches
% -----------------------------------------------------------------
estOpt = branch('Estimation options','estopt',...
{bet2path,fracthresh,vertgrad,headrad,cog,thrsh,verbose});
estOpt.help = {'Options for skull stripping.'}';
wrtOpt = branch('Writing options','wrtopt',...
{fsloutput,nooutput,outline,mask,skull,surfoff});
wrtOpt.help = {'Options for which images to write out.'};
% Output branch
% ------------------------------------------------------------------
opts = branch('Skull Strip using BET2','skullstrip',{source,estOpt,wrtOpt});
opts.prog = @extbrain;
opts.vfiles = @vfiles_extbrain;
opts.help = {'Skull strip a brain using bet2.'};
return
% -----------------------------------------------------------------
% FUNCTIONS
% -----------------------------------------------------------------
function extbrain(varargin)
job = varargin{1};
% Parse Estimation Options
% -----------------------------------------------------------------
f = ['-f ',num2str(job.estopt.fracthr)];
g = [' -g ',num2str(job.estopt.vertgrad)];
if job.estopt.headrad < 0
headrad = [];
else
headrad = ['-r ',num2str(job.estopt.headrad)];
end
if any(job.estopt.cog < 0)
cog = [];
else
cog = ['-c ',mat2str(job.estopt.cog)];
end
% File extention
% -----------------------------------------------------------------
switch job.wrtopt.fslout
case 'ANALYZE'
ext = '.img';
case 'NIFTI'
ext = '.nii';
end
% output strings
% -------------------------------------------------------------------
[wrtParams{1:5,1}] = deal('No');
if isempty(job.estopt.thrsh)
thrshStr = 'No';
else
thrshStr = 'Yes';
end
% assemble all options.
% ---------------------------------------------------------------------
options = [f, g, headrad, cog, job.estopt.thrsh, job.estopt.verbose, ...
job.wrtopt.noout, job.wrtopt.outln, job.wrtopt.mask, ...
job.wrtopt.skull, job.wrtopt.surf];
% path to bet2
% -------------------------------------------------------------------
bet2Path = job.estopt.bet2path{1};
% Setup FSLOUTPUTTYPE environment variable depending on OS and shell
% --------------------------------------------------------------------
if ispc
setFSL = ['set FSLOUTPUTTYPE=',job.wrtopt.fslout,'& '];
else
[s w] = unix('echo $SHELL');
% if s is not 0 then we cannot figure out the shell
if (s == 0)
if ~isempty(regexpi(w,'.*csh$'))
setFSL = ['setenv FSLOUTPUTTYPE ',job.wrtopt.fslout,'; '];
else
% assume shell is sh, ash, ksh, zsh or bash
setFSL = ['export FSLOUTPUTTYPE=',job.wrtopt.fslout,'; '];
end
else
warning('Cannot get your shell type. Assuming sh.')
setFSL = ['export FSLOUTPUTTYPE=',job.wrtopt.fslout];
end
end
% Loop over brains
% ---------------------------------------------------------------
for i = 1:numel(job.brains)
vorig = spm_vol(job.brains{i});
[tmp fn ext] = fileparts(vorig.fname);
fn = [fn,ext];
% Root filenames
% ---------------------------------------------------------------
[pth fn ext] =fileparts(vorig.fname);
origFileRoot = fullfile(pth,fn);
newBETFileRoot = fullfile(pth,['b2',fn]);
if isempty(job.wrtopt.noout)
genFiles.ext = newBETFileRoot;
wrtParams{1} = 'Yes';
end
% Generated file names. To be used to find the files later.
% ------------------------------------------------------------------
if ~isempty(job.wrtopt.outln)
genFiles.ovl = [newBETFileRoot,'_overlay'];
wrtParams{2} = 'Yes';
end
if ~isempty(job.wrtopt.mask)
genFiles.msk = [newBETFileRoot,'_mask'];
wrtParams{3} = 'Yes';
end
if ~isempty(job.wrtopt.skull)
genFiles.skl = [newBETFileRoot,'_skull'];
wrtParams{4} = 'Yes';
end
if ~isempty(job.wrtopt.surf)
genFiles.srf = [newBETFileRoot,'_mesh.off'];
wrtParams{5} = 'Yes';
end
fprintf('Vol %i:\tInput File: %s\n',i,fn);
fprintf('EstOpt:\tf:%0.2f, g:%0.2f, Threshold:%s\n',...
job.estopt.fracthr,job.estopt.vertgrad,thrshStr);
fprintf('Write:\tExtracted:%s, Overlay:%s, Mask:%s, Skull:%s, Mesh:%s\n',wrtParams{:});
% set up a bet compatible image. This may have changed in FSL 3.3
% previously one had to write out a float-type image but this seems not
% be necessary anymore. Although BET2 will produce a float.
% ---------------------------------------------------------------
vbetcomp = vorig;
vbetcomp = rmprivate(vbetcomp);
vbetcomp.fname = [newBETFileRoot,ext];
% check if file is in neurologic or radiologic format. FSL wants
% images stored in left-handed format (radiologic). If the determinant
% of the transformation matrix is positive then probably neurologic so
% flip it.
% ---------------------------------------------------------------
if det(vorig.mat) > 0
% set up flipping transformation
matflip = spm_matrix([0 0 0 0 0 0 -1]);
vbetcomp.mat = matflip*vbetcomp.mat;
end
% Scaling (this is determined by SPM anyway when writing file)
% ---------------------------------------------------------------
vbetcomp.pinfo = [1 0 0]';
% Update user
% ---------------------------------------------------------------
fprintf('Vol %i:\tStep 1/4: Reading original file.\n',i);
% read in the actual original image data
% ---------------------------------------------------------------
yorig = spm_read_vols(vorig);
% remove voxels with values less than 0. These will not be put back
% later on since they shouldn't be there anyway.
% ---------------------------------------------------------------
yorig(yorig < 0) = 0;
% Update user
% ---------------------------------------------------------------
fprintf('Vol %i:\tStep 2/4: Writing BET2 compatible file (orientation:radiologic).\n',i);
% write out the data orientation: radiologic
% ---------------------------------------------------------------
vbetcomp = spm_write_vol(vbetcomp,yorig);
% Update user
% ---------------------------------------------------------------
fprintf('Vol %i:\tStep 3/4: Extracting Brain.\n',i)
% do the skull stripping
% s is a flag for success=0 or failure=non-zero
% w contains any messages.
% ---------------------------------------------------------------
s = [];
w = [];
if ispc
[s w] = dos([setFSL, bet2Path,' ',newBETFileRoot,' ',newBETFileRoot,' ',options]);
else
[s w] = unix([setFSL, bet2Path,' ',newBETFileRoot,' ',newBETFileRoot,' ',options]);
end
if s > 0
flag = 'FAILURE';
else
flag = 'SUCCESS';
end
if isempty(job.estopt.verbose)
fprintf('BET2 returned a status of: %s\n',flag);
else
fprintf('BET2 returned a status of %s, with the following output:\n%s',flag,w);
end
% Set transformations on all the volumes
% -------------------------------------------------------------------------
fprintf('Vol %i:\tStep 4/4: Applying original orientation matrix\n',i);
chkRegFiles = vorig.fname;
outFiles = fieldnames(genFiles);
for j = 1:numel(outFiles)
spm_get_space([genFiles.(outFiles{j}),ext],vorig.mat);
chkRegFiles = strvcat(chkRegFiles,[genFiles.(outFiles{j}),ext]);
end
spm_check_registration(chkRegFiles);
fprintf('\n')
end
% -------------------------------------------------------------------
% REMOVE PRIVATE FIELD FROM MAPPED FILE HEADER
% -------------------------------------------------------------------
function Vout = rmprivate(Vin)
% remove private field from NIFTI volume structure
if isfield(Vin,'private')
Vout = rmfield(Vin,'private');
else
Vout = Vin;
end
% -------------------------------------------------------------------
% SET UP VIRTUAL FILES
% -------------------------------------------------------------------
function vf = vfiles_extbrain(varargin)
job = varargin{1};
switch job.wrtopt.fslout
case 'ANALYZE'
ext = '.img';
case 'NIFTI'
ext = '.nii';
end
if isempty(job.wrtopt.noout)
for i = 1:numel(job.brains)
[pth fn null] =fileparts(job.brains{i});
vf{i} = fullfile(pth,['b2',fn,ext]);
end
end
return
}}}
{{{
function vol = getVBMvol(p)
% GETVOL loads volume files from C. Gaser's VBM scripts and extracts volumes
% vol = GETVBMVOL(P) loads the files specified in P and outputs a
% structure with fields
% vol.subjid subject id (uses NU convention of F + numeric id
% vol.gvol gray matter volume
% vol.wvol white matter volume
% vol.cvol csf volume
% vol.tvol total volume
%
% If no file names are specified a file selection window is presented.
%
% The volumes file must contain values for all 3 tissue classes.
% Authors: Darren Gitelman
% $Id: getVBMvol.m,v 1.0 2006-08-31 01:27:34-05 drg Exp drg $
% initialize variables
idstart = 'F';
vol = struct('subjid', [],...
'gvol', [],...
'wvol', [],...
'cvol', [],...
'tvol', []);
% choose files
if nargin == 0
p = spm_select(Inf,'txt$','Select volumes text files.');
end
for i = 1:size(p,1)
% find the subject ID
[r s]= strtok(deblank(p(i,:)),idstart);
c = cellstr(s');
c1 = cellfun(@str2num,c,'uniformoutput',false);
c2 = cellfun('isempty',c1');
% assume that is a number terminated by non-numeric characters
idend = min(find(c2(2:end)));
% load the volume file (should contain only 3 numbers)
tmp = load(deblank(p(i,:)));
if ~numel(tmp) == 3
error('Volumes file must contain all 3 tissue classes');
end
% fill in the struct
vol(i).subjid = ['F',s(2:idend)];
vol(i).gvol = tmp(1);
vol(i).wvol = tmp(2);
vol(i).cvol = tmp(3);
vol(i).tvol = vol(i).gvol + vol(i).wvol + vol(i).cvol;
end
}}}
{{{
function [xyz extvalues] = getclustldata(datasource)
% GETCLUSTDATA Gets the values from a file for all voxels in a cluster
% [XYZ EXTVALUES] = GETCLUSTDATA(DATASOURCE) returns the voxel locations
% XYZ, and extracted data values, EXTVALUES, for all voxels in the
% currently selected cluster. Data is extracted from a single image,
% DATASOURCE, which can be a filename or volume structure of an image.
% If DATASOURCE is not provided the user is prompted. The base workspace
% must contain the variable xSPM, which should be present if results are
% being displayed in SPM.
%
% SPM2 or SPM5 must be in the matlab path, and SPM defaults should have
% already been set appropriately.
%
% Portions of the code extracted from spm_list.m
% Authors: Darren Gitelman
% $Id: getclustdata.m,v 1.0 2007-01-02 08:51:20-06 drg Exp drg $
% initialize output variables
%--------------------------------------------------------------
xyz = [];
extvalues = [];
% Get the xSPM structure from the base workspace
%--------------------------------------------------------------
evalin('base','assignin(''caller'',''mySPM'',xSPM)');
% Jump to and get current voxel location (x,y,z) in mm
%--------------------------------------------------------------
[xyzmm,i] = spm_XYZreg('NearestXYZ',...
spm_results_ui('GetCoords'),mySPM.XYZmm);
spm_results_ui('SetCoords',mySPM.XYZmm(:,i));
% find the cluster
%--------------------------------------------------------------
A = spm_clusters(mySPM.XYZ);
j = find(A == A(i));
% get actual voxels
%--------------------------------------------------------------
xyz = mySPM.XYZ(:,j);
% get the image to extract data from
%--------------------------------------------------------------
if nargin < 1
switch lower(spm('ver'))
case 'spm2'
datasource = spm_get(1,'IMAGE','Select image file for data extraction.');
case 'spm5'
datasource = spm_select(1,'image','Select image file for data extraction.');
otherwise
error('Unknown version of SPM.')
end
elseif ischar(datasource)
datasource = spm_vol(datasource);
end
extvalues = spm_get_data(datasource,xyz);
return;
}}}
{{{
function PSTH = getpsth(xY,SPMfile,window,bin,events,sessions,adjcurr,BETAHACK)
% GETPSTH Extract data from specified events and sessions using FIR model
% PSTH = GETPSTH(XY,SPMFILE,WINDOW,BIN,EVENTS,SESSIONS,ADJCURR,BETAHACK)
% returns a structure containing data for plotting a peristimulus time
% histogram. This differs from the machinery in SPM in that it can
% extract data for several events at once and across sessions.
%
% SPM MUST BE IN THE MATLAB PATH. SHOULD WORK WITH BOTH SPM2 AND SPM5 BUT
% IT HAS NOT BEEN FORMALLY TESTED WITH SPM5.
%
% Assumes the same number of columns per session in the design matrix.
% Inputs: xY Can be either a structure similar to that from
% spm_regions, or an [x y z] vector specifying a point.
% SPMfile Name of SPM file.
% window Length of psth to model.
% bin Size of bin for psth in secs (Usually the same as the
% TR.
% events Event numbers to extract. This corresponds to the
% order listed in SPM.Sess.U.
% sessions Session numbers to extract
% adjcurr Flag of whether to adjust for other event-types in
% the same session.
% BETAHACK Value to limit betas in case of poor FIR estimates.
%
% NOTE NOTE NOTE: Cannot deal with sessions that have different event
% types. It assumes that all event types are present in all sessions!!!
%
% The function can be called non-interactively by specifying all the
% inputs.
%
% Example
%
% PSTH = getpsth([-21 -69 57],fullfile(pwd,'SPM.mat'),32,2.1,2,1,1)
% This would extract data from the SPM.mat file in the current directory,
% at the specfied point, with a window of 32 secs, bin of 2.1 secs, for
% event 2, session 1, and full adjustments as in spm_graph.
%
% Output: PSTH Structure containg the extracted data.
% SPMfile same as input.
% xY Extracted VOI info
% window Specified window length in secs
% bin Specified psth bin. Note that the actual window and
% bin size may differ from the specified size and is
% based on the the requirements of setting up the FIR
% basis.
% adjcurr Flag of whether to adjust for other columns in the
% current session. (i.e., columns other than the event-
% type specified.
% pst Peristimulus time vector
% events Structure of data for each event type.
% number Number of the extracted event, based on SPM.Sess.U
% name Name of the extracted event.
% sessions Extracted sessions for each event. Same as input.
% order corresponds to columns in psth.
% psth The extracted psth for each session in columns
% sem Standard error of the mean for each time point
% pci Confidence interval for each time point.
%
% Example
% To plot the PSTH with 95% confidence interval errorbars
%
% errorbar(PSTH.pst,PSTH.events(1).psth,PSTH.events(1).pci)
%
% To plot the first 2 events, with S.E. errorbars, and a legend.
% Use n to index the structure so to avoid rewriting the command each
% time.
%
% n = 1;
% errorbar(PSTH.pst,PSTH.events(n).psth,PSTH.events(n).sem)
% hold on
% n = 2;
% errorbar(PSTH.pst,PSTH.events(n).psth,PSTH.events(n).sem)
% legend(PSTH.events(1).name,[PSTH.events(1).name, ' S.E.'],...
% PSTH.events(2).name,[PSTH.events(2).name, ' S.E.'])
% Note that each plotted line gets 2 entries in the legend because matlab
% insists on having a legend entry for both a line and its errorbars.
%
% Multiple sessions can be averaged. PSTH.events(n).psth is organized
% as rows = time bins and cols = sessions. In order to average across
% sessions, and not time bins, the matrix must be transposed, then
% averaged, then transposed back. To plot the first event for the
% average psth across sessions:
%
% plot(PSTH.pst,mean(PSTH.events(1).psth')')
% Based on spm_graph and the extract_psth code from Alexa Morcom.
% Author: Darren Gitelman
% $Id: getpsth.m 164 2011-07-12 22:01:26Z drg $
% Check for spm defaults
% --------------------------------------------------------------
var = whos('global');
var = str2mat(var.name);
if ~strmatch('defaults',var)
error('SPM defaults not present. Please start spm or run spm_defaults first.');
end
% Find or create the interactive window
% --------------------------------------------------------------
Finter = spm_figure('Findwin','Interactive');
if isempty(Finter)
Finter = spm('CreateIntWin');
end
spm_input('!SetNextPos',1);
% PSTH structure
% --------------------------------------------------------------
PSTH = struct(...
'SPMfile', '',...
'adjcurr', [],...
'bin', [],...
'pst', [],...
'window', [],...
'xY', [],...
'events', struct(...
'name', '',...
'number', [],...
'pci', [],...
'psth', [],...
'sem', [],...
'sessions', [],...
'bad', struct(...
'pci', [],...
'psth', [],...
'sem', [],...
'sessions', []...
)... % close "bad" struct
)... % close "events" struct.
); % close "PSTH" struct
% CHECK INPUT ARGUMENTS
% --------------------------------------------------------------
if nargin >= 2
[pth, fn, ext] = fileparts(SPMfile);
cd(pth)
load('SPM.mat')
else
SPM = [];
end
if nargin < 1
xY = [];
[PSTH.xY, SPM, SPMfile] = my_regions(xY,SPM);
else
if ~isstruct(xY)
if numel(xY) == 3;
xY.xyz = xY;
end
end
[PSTH.xY, SPM, SPMfileTmp] = my_regions(xY,SPM);
end
if ~exist('SPMfile','var')
SPMfile = [];
end
if isempty(SPMfile)
SPMfile = SPMfileTmp;
end;
if isempty(SPMfile)
switch spm('ver')
case 'SPM2'
SPMfile = spm_get(1,'SPM.mat','Select SPM.mat');
case {'SPM5','SPM8'}
SPMfile = spm_select(1,'^SPM\.mat$','Select SPM.mat');
otherwise
error('Unknown version of SPM.');
end
PSTH.SPMfile = SPMfile;
else
PSTH.SPMfile = SPMfile;
end
[pth fn ex]=fileparts(SPMfile);
if ~isempty(pth)
cd(pth)
else
return
end
load(SPMfile);
spm_input('!SetNextPos',1);
if nargin < 4
% do not check if nargin < 3 so we'll get window and bin together.
% -----------------------------------------------------------------
PSTH.window = spm_input('length of psth in seconds','!+1','e',32);
PSTH.bin = spm_input('size of each bin in seconds','!+1','e',SPM.xY.RT);
else
PSTH.window = window;
PSTH.bin = bin;
end
if nargin < 5
% the code assumes that all sessions have at least some representation
% of all event types so we use Sess(1) as an example
% --------------------------------------------------------------------
str = [];
k = 0;
str = sprintf('The event-types are:');
% NOTE: THIS DOES NOT DEAL WITH PARAMETRIC EFFECTS!!! this may be a
% silly statment though, since it's not clear that one can plot
% parameteric effects.
% MORE IMPORTANTLY IT WILL NOT WORK IF THE DIFFERENT SESSIONS DO NOT
% ALL HAVE THE SAME EVENT TYPES!!!
for i = 1:size(SPM.Sess(1).U,2)
str = str2mat(str,sprintf('[%d] %s',i,char([SPM.Sess(1).U(i).name{1}])));
end
% show the event list as a help dialog
% --------------------------------------------------------------
delete(findobj('Tag','eventWindow'));
h = helpdlg(str,'Event-Types');
set(h,'Tag','eventWindow');
figure(Finter);
events = spm_input('enter event-types','!0','e',[]);
try
delete(h)
catch
% nothing to delete.
end
end
if nargin < 6
sessions = spm_input(sprintf('enter sessions [1..%d]',size(SPM.Sess,2)),...
'!+1','e',[]);
end
if nargin < 7
% Adjust for in session effects
% Yes should be the default.
% -----------------------------------------------------------------
PSTH.adjcurr = spm_input('Adjust for other in-session effects?','!+1','y/n',[1 0],1);
else
PSTH.adjcurr = adjcurr;
end
% This option is hidden from users using the GUI. Advanced users will deal
% with this by changing value or using command line.
% --------------------------------------------------------------------
if nargin == 7
% BETAHACK This variable sets a limit to the beta value. I found that
% occasionally the least squares equations return an incorrect or
% wildly high value if there is no effect at a voxel or if only a few
% events are being used to estimate the effect (2 or less). This value
% tries to put these beta values in a BAD value field. For my data 100
% seemed to work. Your data may be different. For no restrictions, just
% set to Inf.
% -----------------------------------------------------------------
PSTH.BETAHACK = BETAHACK;
else
PSTH.BETAHACK = 100;
end
%--------------------------------------------------------
% Now do the work
%--------------------------------------------------------
for n = 1:length(events)
ev = events(n);
PSTH.events(n).number = ev;
% The function assumes that all event-types are
% present in all sessions.
% -----------------------------------------------------------------
PSTH.events(n).name = SPM.Sess(1).U(ev).name;
for m = 1:length(sessions)
ses = sessions(m);
xBF = SPM.xBF;
U = SPM.Sess(ses).U(ev);
% event vector in microtime. The event vector is always first in
% U.u and can be followed by vectors for parametric effects.
% -----------------------------------------------------------
U.u = U.u(:,1);
xBF.name = 'Finite Impulse Response';
xBF.order = round(PSTH.window/PSTH.bin);
xBF.length = xBF.order*PSTH.bin;
xBF = spm_get_bf(xBF);
xBF.bin = xBF.length/xBF.order;
if isempty(PSTH.pst)
j = round(xBF.length/xBF.bin);
PSTH.pst = [1:j] * xBF.bin - xBF.bin/2;
end
X = spm_Volterra(U,xBF.bf,1);
k = SPM.nscan(ses);
% FIR basis in macrotime
% -------------------------------------------------------------
X = X((0:(k - 1)) * SPM.xBF.T + SPM.xBF.T0 + 32,:);
% jX will index the appropriate rows of the design. It will
% increment for different sessions for example.
% -------------------------------------------------------------
jX = SPM.Sess(ses).row;
if PSTH.adjcurr
% if adjcurr is true that means the user wants to adjust for other
% columns in the same session as our chosen event. In that case iX
% will just be the columns in the original design associated with
% the chosen event. These are then set to 0 but all the other
% columns for the session will be present and hence adjusted
% for
% ---------------------------------------------------------
iX = SPM.Sess(ses).col(SPM.Sess(ses).Fc(ev).i);
else
% if adjcurr is false then we don't want to adjust for other columns in
% the same session. So then iX is now equal to all the columns
% in a session which are then set to 0.
% ---------------------------------------------------------
iX = SPM.Sess(ses).col([SPM.Sess(ses).Fc.i]);
end
% iX0 represents all the columns of the design but with the columns
% of the selected event type removed, i.e., remove iX from iX0
% -------------------------------------------------------------
iX0 = 1:size(SPM.xX.X,2);
iX0(iX) = [];
% get the selected rows and columns of the design. Note that this
% moves the effects of interest to the head of the class (i.e., the
% first set of columns of the matrix.
% -------------------------------------------------------------
X = [X SPM.xX.X(jX,iX0)];
% whiten the design
% -------------------------------------------------------------
X = SPM.xX.W(jX,jX)*X;
% add the filter matrix
% -------------------------------------------------------------
X = [X SPM.xX.K(ses).X0];
% Re-estimate to get PSTH and CI
%------------------------------------------------------
CI = 1.6449; % = spm_invNcdf(1-0.05);
dt = U.dt;
j = xBF.order;
xX = spm_sp('Set',X);
pX = spm_sp('x-',xX);
betas = pX*PSTH.xY.u(jX);
res = spm_sp('r',xX,PSTH.xY.u(jX));
df = size(X,1) - size(X,2);
bcov = pX*pX'*sum(res.^2)/df;
betas = betas(1:j)/dt;
sem = sqrt(diag(bcov(1:j,(1:j))))/dt;
pci = CI*sem;
% this is the hack to trap for wildly incorrect betas. This seems
% to happen if a voxel has no effects or there are very few events
% The cutoff chosen is liberal for my data but may not be correct
% for yours
% -------------------------------------------------------------
if max(abs(betas)) < PSTH.BETAHACK
PSTH.events(n).pci = [PSTH.events(n).pci, pci];
PSTH.events(n).psth = [PSTH.events(n).psth, betas];
PSTH.events(n).sem = [PSTH.events(n).sem, sem];
PSTH.events(n).sessions = [PSTH.events(n).sessions, sessions(m)];
else
PSTH.events(n).bad.pci = [PSTH.events(n).bad.pci, pci];
PSTH.events(n).bad.psth = [PSTH.events(n).bad.psth, betas];
PSTH.events(n).bad.sem = [PSTH.events(n).bad.sem, sem];
PSTH.events(n).bad.sessions = [PSTH.events(n).bad.sessions, sessions(m)];
end
end
end
return
%------------------------------------------------------------------------
function [xY, SPM, SPMfile] = my_regions(xY,SPM)
% MY_REGIONS allows a user defined point or volume of interest. It returns
% a region structure containing the information about the region and its
% signal. NOTE: The entire time series is returned for later
% processing. It has been whitened and filtered.
%
% xY - VOI structure (THIS IS SIMILAR BUT NOT IDENTICAL TO xY PRODUCED
% BY SPM_REGIONS).
% xY.xyz - centre of VOI {mm}
% xY.name - name of VOI
% xY.def - VOI definition
% xY.spec - VOI definition parameters
% xY.XYZmm - MM coordinates of all voxels in VOI
% xY.XYZ - Voxel coordinates of all voxels in VOI
% xY.y - [whitened and filtered] voxel-wise data
% xY.u - first eigenvariate {scaled - c.f. mean response}
% SAME AS Y
% xY.v - first eigenimage
% xY.s - eigenvalues
if nargin < 1; xY = []; end;
if nargin < 2; SPM = []; end;
SPMfile = [];
if isempty(xY)
xY.xyz = spm_input('center of voi','!+1','e',[],3);
xY.xyz = xY.xyz(:);
xY.name = spm_input('name of region','!+1','s','VOI');
xY.def = spm_input('VOI definition...','!+1','b',...
{'point','sphere','box'});
if strcmp(xY.def,'sphere') || strcmp(xY.def,'box')
% assume we will need a result to get the voxels
% unfortunately spm_getSPM is not scriptable
% -------------------------------------------------------------
spm_input('!SetNextPos',1);
[SPM,xSPM] = spm_getSPM;
Q = ones(1,size(xSPM.XYZmm,2));
SPMfile = fullfile(SPM.swd,'SPM.mat');
else
if isempty(SPM)
[SPM, SPMfile] = load_spm;
end
end
% mm to voxel matrix
% -------------------------------------------------------------
iM = SPM.xVol.iM;
switch xY.def
case 'point'
xY.spec = 0;
xY.XYZ = iM * [xY.xyz; 1];
xY.XYZ = xY.XYZ(1:3);
xY.XYZmm = xY.xyz;
case 'sphere'
%---------------------------------------------------------------
if ~isfield(xY,'spec')
xY.spec = spm_input('VOI radius (mm)','!+0','r',0,1,[0,Inf]);
end
d = [ xSPM.XYZmm(1,:) - xY.xyz(1);...
xSPM.XYZmm(2,:) - xY.xyz(2);...
xSPM.XYZmm(3,:) - xY.xyz(3) ];
Q = find(sum(d.^2) <= xY.spec^2);
xY.XYZ = xSPM.XYZ(:,Q);
xY.XYZmm = xSPM.XYZmm(:,Q);
case 'box'
%---------------------------------------------------------------
if ~isfield(xY,'spec')
xY.spec = spm_input('box dimensions [x y z] {mm}',...
'!+0','r','0 0 0',3);
end
Q = find(all(abs(xSPM.XYZmm - xY.xyz*Q) <= xY.spec(:)*Q/2));
xY.XYZ = xSPM.XYZ(:,Q);
xY.XYZmm = xSPM.XYZmm(:,Q);
end
elseif isstruct(xY)
% at this point we just assume that the user wants a point.
% -------------------------------------------------------------
if isfield(xY,'xyz')
if isempty(SPM)
[SPM, SPMfile] = load_spm;
end
xY.xyz = xY.xyz(:);
xY.spec = 0;
xY.def = 'point';
if ~isfield(xY,'name')
xY.name = 'VOI';
end
% mm to voxel matrix
% -------------------------------------------------------------
iM = SPM.xVol.iM;
xY.XYZ = iM * [xY.xyz; 1];
xY.XYZ = xY.XYZ(1:3);
xY.XYZmm = xY.xyz;
end
else
error('Not enough info to set up rest of xY structure');
end
%-Extract required data from results files
%=======================================================================
%-Get raw data, whiten and filter
%-----------------------------------------------------------------------
y = [];
try
y = spm_get_data(SPM.xY.VY,xY.XYZ);
if isempty(y)
% whoopsie no data
% -------------------------------------------------------------
error('No data in selected region.')
end
% whiten and filter
% -------------------------------------------------------------
y = spm_filter(SPM.xX.K,SPM.xX.W*y);
catch
try
% remap files in SPM.xY.P if SPM.xY.VY is no longer valid
%-------------------------------------------------------
sprintf('Remapping data...')
SPM.xY.VY = spm_vol(SPM.xY.P);
y = spm_get_data(SPM.xY.VY,xY.XYZ);
if isempty(y)
% whoopsie no data
% ----------------------------------------------------------
error('No data in selected region.')
end
y = spm_filter(SPM.xX.K,SPM.xX.W*y);
catch
% data has been moved or renamed
%-------------------------------------------------------
y = [];
spm('alert!',{'Original data have been moved or renamed',...
'Recomendation: please update SPM.xY.P'},...
mfilename,0);
end
end
% compute regional response in terms of first eigenvariate
%-----------------------------------------------------------------------
[m n] = size(y);
if m > n
[v s v] = svd(spm_atranspa(y));
s = diag(s);
v = v(:,1);
u = y*v/sqrt(s(1));
else
[u s u] = svd(spm_atranspa(y'));
s = diag(s);
u = u(:,1);
v = y'*u/sqrt(s(1));
end
d = sign(sum(v));
u = u*d;
v = v*d;
Y = u*sqrt(s(1)/n);
% set in structure
%-----------------------------------------------------------------------
xY.y = y;
xY.u = Y;
xY.v = v;
xY.s = s;
%========================================================================
function [SPM, SPMfile] = load_spm()
% Loads and SPM.mat fiile and returns the filename and the SPM structure
% itself.
SPM = [];
SPMfile = [];
switch spm('ver')
case 'SPM2'
SPMfile = spm_get(1,'SPM.mat','Select SPM.mat');
case {'SPM5','SPM8'}
SPMfile = spm_select(1,'^SPM\.mat$','Select SPM.mat');
otherwise
error('Unknown version of SPM.');
end
[pth fn ex] = fileparts(SPMfile);
if ~isempty(pth)
cd(pth)
else
return
end
load(SPMfile);
spm_input('!SetNextPos',1);
}}}
Convert a hdr+img+[mat] file to an nii image
{{{
function img2nii(p)
% IMG2NII converts hdr+img+[mat] files to nii image
% IMG2NII(P) P are image names to convert. If not fully qualified it
% will write the image to the current directory. If P is not supplied
% the user will be prompted to select the image file to process.
% SPM5 functions must be in the Matlab path.
% Author: Darren Gitelman
% $Id: img2nii.m,v 1.0 2008-11-28 13:11:03-06 drg Exp drg $
if nargin == 0
p = spm_select(inf,'image','Select image file(s)');
if isempty(p)
return
end
end
for i = 1:size(p,1)
[D F E] = fileparts(deblank(p(i,:)));
v = spm_vol(fullfile(D,[F E]));
img = spm_read_vols(v);
% conversion is effected in SMP by simply by changing the filename
% extension and writing out the data with the new filename
% -------------------------------------------------------
v.fname = fullfile(D,[F,'.nii']);
v = spm_create_vol(v);
spm_write_vol(v,img);
fprintf('Converted %s to %s\n',[F E],[F,'.nii'])
end
fprintf('done\n')
}}}
This function averages a set of images. It must be invoked as a toolbox in [[SPM5|http://www.fil.ion.ucl.ac.uk/spm/software/spm5/]]. It is not designed to work in earlier versions of SPM. To use, make a directory called "mean_img" inside the spm5/toolbox folder. Save the function below (copy and paste) as nbmg_config_meanimg.m inside this folder. Restart Matlab and [[SPM5|http://www.fil.ion.ucl.ac.uk/spm/software/spm5/]]. Access the toolbox from the TASKS menu at the top right of the Graphics window. It is under Tools.
{{{
function job = nbmg_config_meanimg
% NBMG_CONFIG_ MEANIMG averages a set of images
% Average is implemented by adding all the images together and then
% setting the scaling factor in pinfo appropriately (i.e., 1/n, where
% n = number of images.
%
% The images must have the same dimensions, orientations (as defined by
% the Origin header field or any associated *.mat files), and voxel sizes.
%
% This is not a "softmean" - zero voxels are treated as zero.
%
% This function returns a job structure for SPM5, and runs the job when
% called by spm_jobman.
%
% Portions of this code are derived from spm_mean_ui by John Ashburner
% and Andrew Holmes
% Author: Darren Gitelman
% $Id: nbmg_config_meanimg.m,v 1.1 2007-05-16 12:13:56-05 drg Exp drg $
entry = inline(['struct(''type'',''entry'',''name'',name,'...
'''tag'',tag,''strtype'',strtype,''num'',num)'],...
'name','tag','strtype','num');
files = inline(['struct(''type'',''files'',''name'',name,'...
'''tag'',tag,''filter'',fltr,''num'',num)'],...
'name','tag','fltr','num');
branch = inline(['struct(''type'',''branch'',''name'',name,'...
'''tag'',tag,''val'',{val})'],...
'name','tag','val');
mnu = inline(['struct(''type'',''menu'',''name'',name,'...
'''tag'',tag,''labels'',{labels},''values'',{values},''help'',hlp)'],...
'name','tag','labels','values','hlp');
addpath(fullfile(spm('dir'),'toolbox','meanimg'));
inputImgs = files('Images to average','inpimg','image',[1 Inf]);
inputImgs.help = {'Select images to average.'};
outputImgs = entry('Name of mean image','outpimg','s',[1 1]);
outputImgs.val = {'meanimage.img'};
outputImgs.help = {'Enter a filename for the mean image.'};
flag = mnu('Mask zeros to 0','flag',{'No','Yes'},{[],'m'},...
{['''m'' - masks the mean to zero or NaN wherever ',...
'a zero occurs in the input images.']});
flag.val = {'m'};
job = branch('Mean images','avgimg',{inputImgs,outputImgs,flag});
p1 = ['NBMG_CONFIG_ MEANIMG averages a set of images ',...
'Average is implemented by adding all the images together and ',...
'then setting the scaling factor in pinfo appropriately ',...
'(i.e., 1/n, where n = number of images.'];
p2 = ['The images must have the same dimensions, orientations ',...
'(as defined by the Origin header field or any associated *.mat ',...
'files), and voxel sizes.'];
p3 = ['This is not a "softmean" - zero voxels are treated as zero.'];
p4 = ['This function returns a job structure for SPM5, and runs the job ',...
'when called by spm_jobman. '];
p5 = ['Portions of this code are derived from spm_mean_ui by ',...
'John Ashburner and Andrew Holmes.'];
p6 = ['Author: Darren Gitelman ',...
'$Id: nbmg_config_meanimg.m,v 1.1 2007-05-16 12:13:56-05 drg Exp drg $'];
job.help = {p1,'',p2,'',p3,'',p4,'',p5,'',p6};
job.prog = @meanimgs;
job.vfiles = @vfiles_mean;
% ===================================================================
function meanimgs(varargin)
job = varargin{1};
% map input images
Vi = spm_vol(char(job.inpimg));
% fixup output filename
[pth fn ext] = fileparts(job.outpimg);
if ~strcmpi(ext,'.img')
fn = [fn,ext,'.img'];
else
fn = [fn,'.img'];
end
n = numel(Vi);
if n==0,
error('\t%s : no images selected\n\n',mfilename)
end
if n>1 && any(any(diff(cat(1,Vi.dim),1,1),1))
error('images don''t all have same dimensions')
end
% if any(any(any(diff(cat(3,Vi.mat),1,3),3)))
% error('images don''t all have same orientation & voxel size')
% end
%-Compute mean and write headers etc.
%-----------------------------------------------------------------------
pth = fileparts(Vi(1).fname);
fprintf(' ...computing ')
Vo = struct('fname', fullfile(pth,fn),...
'dim', Vi(1).dim(1:3),...
'dt', [4, spm_platform('bigend')],...
'mat', Vi(1).mat,...
'pinfo', [1.0,0,0]',...
'descrip', 'spm - mean image');
%-Adjust scalefactors by 1/n to effect mean by summing
for i=1:numel(Vi)
Vi(i).pinfo(1:2,:) = Vi(i).pinfo(1:2,:)/n;
end;
Vo = spm_create_vol(Vo);
if isempty(job.flag)
Vo.pinfo(1,1) = spm_add(Vi,Vo);
else
Vo.pinfo(1,1) = spm_add(Vi,Vo,job.flag);
end
Vo = spm_create_vol(Vo);
%-End - report back
%-----------------------------------------------------------------------
fprintf(' ...done\n')
fprintf('\tMean image written to file ''%s'' in current directory\n\n',Vo.fname)
function vf = vfiles_mean(job)
% fixup output filename
[pth fn ext] = fileparts(job.outpimg);
if ~strcmpi(ext,'.img')
fn = [fn,ext,'.img'];
else
fn = [fn,'.img'];
end
pth = fileparts(job.inpimg{1});
fname = fullfile(pth,fn);
vf{1} = fname;
}}}
A function for plotting points. Helpful for visualizing centroids of activation on glass brains or for meta-analyses.
{{{
function pthandles = mip_maker(varargin)
% MIP_MAKER Displays a plot of XYZ coordinates on an SPM glass brain.
%
% FORMAT mip_maker(Action,XYZ,t,c,V)
% XYZ - [n x 3] a matrix of coordinates of points (MNI coordinates standard)
% Talairach coordinates can be used but the underlying grid comes from SPM which
% is based on MNI as the normalization standard.
% t - [n x 3] Color of dot to plot. Must be same size as XYZ or do not include it.
% If t is not included then all points are plotted in red.
%
% c - [n x 1] cell array of characters to plot
% These are some good TEX characters to use:
% \bullet
% \otimes
% \oplus
% \oslash
% \o
% \times
% Also any of the standard font characters can be used.
% If c is not included then \bullet is assumed.
%
% V - [n x 9} vector of image and voxel sizes, and origin
%
% If V is not included it is assumed to be
% [53 63 46 3 3 3 27 38 18] or normalized (MNI space) 3x3x3 voxels
% [ DIM VOX ORIGIN ]
%
% pthandles is an n x 3 list of handles for each point plotted
%
% Action strings and formats are listed below
%
% pthandles = mip_maker('Display',[XYZ],[t],{c},[V]) or
% pthandles = mip_maker('d',[XYZ],[t],{c},[V])
% Displays the points supplied
% NOTE: straight brackets around XYZ, t and V and curly brackets around c.
% These are necessary.
%
% The displayed points are ACTIVE. Clicking on them will show their xyz
% coordinates.
%
% NOTE- MIP_MAKER WILL NOT AUTOMATICALLY CLEAR POINTS EACH TIME IT IS RUN.
% POINTS MUST BE EXPLICITLY CLEARED. THIS BEHAVIOR IS TO ALLOW ADDING
% TO EXISTING POINTS.
%
% pthandles = mip_maker('Clear') or mip_maker('c')
% Clears all points.
%
% pthandles = mip_maker('Load',[filename]) or pthandles = mip_maker('l',[filename])
% Loads or asks for a file to load
%
% filename - (Input) filename to load. Points are then displayed
% The filename option is optional. If it is not included the user is prompted
% to choose a file. The uigetfile function is used.
%
% The text file could be made in Excel, for example. Tab-delimited format must
% be used. The column order is
% x y z red blue green char_type dim_x dim_y dim_z vox_x vox_y vox_z origin_x origin_y origin_z
%
% The file must include the first 3 columns but the rest are up to the user as with
% calling the function from the command line.
%
% For example:
% ------------------------------
% -20 -30 18 1 0 0
% 40 3 42 0 0 1
% -----------------------------
% If the above were saved as a text file then 2 points would be plotted in
% standard MNI space, a red one at -20 -30 18, and a blue one at 40 3 42.
%
% Handles of associated points are now stored as the first and second
% elements in the cell array of each points userdata. The third element
% is the xyz coordinate of that point. Clicking on a point displays
% its coordinates at the bottom left of the display.
%
% TAGS OF IMPORTANCE:
%-----------------------------------
% Mip_Maker figure window: mipmaker
% Main axes: hMIPaxes
% Points on axial grid: X1
% Points on sagittal grid: X2
% Points on coronal grid: X3
%
% GLASS-BRAIN GRID
%-----------------------------------
% MIP_MAKER also needs the glass brain grid for plotting. In SPM8 and SPM5
% that file is called MIP.mat. Please edit the line below starting with:
% MIPFILE to point to the location of an SPM MIP.mat file or mip_maker will
% ask you to select the file when plotting.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% IF MIP_MAKER IS USED IN YOUR RESEARCH PLEASE CITE
% Darren Gitelman, Northwestern University, Chicago, IL
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This software is free but copyright (1998-2010) by D. Gitelman, and is
% distributed under the terms of the GNU General Public Licence.
% http://www.gnu.org/copyleft/.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Portions of this code were derived from the SPM96 software
% Wellcome Department of Imaging Neuroscience
% http://www.fil.ion.ucl.ac.uk/spm
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% No warranty of fitness for any purpose is expressed or implied.
% There is no warranty that the software will be maintained, although bug
% reports may be sent to <d-gitelman at northwestern dot edu>
% This sofware should not be used for any purpose that involves the
% diagnosis or treatment of patients.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%=======================================================================
% Author: Darren Gitelman
% $Id: mip_maker.m,v 1.9 2006-05-18 21:47:57-05 drg Exp drg $
% Modified by DRG on 10-11-2010.
%-Global Parameters
%=======================================================================
% Edit the line below to point to the location of an SPM MIP.mat file
MIPFILE = '';
if nargin == 0
fprintf('\n No parameters included. Exiting \n\n');
pthandles = [];
return
end
S = get(0,'ScreenSize');
%WS = [S(3)/1024 S(4)/768 S(3)/1024 S(4)/768];
% Scale screen size based on 1152 x 900 screen
%------------------------------------------------------------------------
WS = [S(3)/1152 S(4)/900 S(3)/1152 S(4)/900];
GRID = .1;
Fontsz = 12;
%-Axis offsets for 3d MIPs: See spm_project.c for derivation
%-----------------------------------------------------------------------
% pixels adjusted empirically for dot character.
Po1 = 127-4;
Po2 = 182+182-91+3;
Po3 = 182-73+3;
Po4 = 218+91-3;
Fgraph = findobj('tag','mipmaker');
if isempty(Fgraph)
Fgraph = figure('Position',[1 2 .25 .4].*WS.*...
[S(1)+50 S(2)+100 S(3) S(4)],...
'Resize','off',...
'PaperPosition',[.75 1.5 7 9.5],...
'PaperType','usletter',...
'DefaultTextFontSize',2*round(12*min(WS)/2),...
'Color','w',...
'Tag','mipmaker',...
'ColorMap',gray(64),...
'Resize','on');
set(Fgraph,'windowbuttondownfcn',['me = gco;',...
'tag = get(me,''tag'');',...
'if strcmp(tag,''X1'') | strcmp(tag,''X2'') | strcmp(tag,''X3''),',...
'h=get(gcf,''userdata'');',...
'udata = get(me,''userdata'');',...
'xyz = udata{3};',...
'set(h,''string'',sprintf(''xyz = [ %0.1f %0.1f %0.1f ]'',xyz));',...
'end'])
set(Fgraph,'windowbuttonupfcn',['h = get(gcf,''userdata'');',...
'set(h,''string'','''')'])
h=text(-.05,.05,'');
set(h,'fontweight','demi')
set(Fgraph,'userdata',h)
axis('off')
whitebg(Fgraph,'w')
else
figure(Fgraph)
end
% end
%end
%-Load and Display MIP
%-----------------------------------------------------------------------
if isempty(findobj('Tag','hMIPaxes'))
set(Fgraph,'Units','normalized');
hMIPaxes = axes('Position',[-1.1 0.02 3.2 .95],...
'Visible','off','Tag','hMIPaxes');
if exist(MIPFILE) ~= 2
% could not load MIPFILE. Let's hope SPM is in the Matlab path
% and try to load it that way.
[spmlocation tmp] = fileparts(which('spm'));
MIPFILE = fullfile(spmlocation,'MIP.mat');
if exist(MIPFILE) ~= 2
% could not find SPM or MIP.mat is not in directory. Ask
% user to select it.
MIPFILE = selectMIPFile;
end
end
load(MIPFILE)
try
mip = mip96*GRID;
catch
error('MIP.mat file must contain a variable called mip96, which contains the glass brain grid.')
end
h =image(rot90((1 - mip)*64)); axis image; axis off;
set(h,'Tag','hMIPaxes')
end
if nargin == 0, Action='clear'; else Action = varargin{1}; end
Action = lower(Action);
switch Action
%-Clear the individual points and all handles
%-----------------------------------------------------------------------
case {'clear', 'c'}
figure(Fgraph)
try
handles = findobj(Fgraph,'Tag','X1');
catch
end
try
h = findobj(Fgraph,'Tag','X2');
handles = [handles h];
catch
end
try
h = findobj(Fgraph,'Tag','X3');
handles = [handles h];
hold on
delete(handles);
hold off
catch
end
clear handles
return
%-Load a text file.
%-----------------------------------------------------------------------
case {'load', 'l'}
if nargin == 1
[fname, pname] = uigetfile('*.*', 'Choose a text file to load');
fname = fullfile(pname,fname);
cd(pname);
else
fname = varargin{2};
end
% Open file and read first line to figure out how many elements are
% present based on the number of tabs
%---------------------------------------------------------------------
fid = fopen(fname);
if fid >0
s = fgetl(fid);
idx = find(s==9); % assume tab is the delimiter
fclose(fid);
else
fprintf('Could not open requested file.\n')
return
end
switch length(idx)
case 2
[x y z] = ...
textread(fname,'%f %f %f');
XYZ = [x y z];
t = repmat([1 0 0],size(XYZ,1),1);
c = repmat({'\bullet'},size(XYZ,1),1);
V = repmat([53 63 46 3 3 3 27 38 18],size(XYZ,1),1);
case 5
[x y z t1 t2 t3] = ...
textread(fname,'%f %f %f %f %f %f');
XYZ = [x y z];
t = [t1 t2 t3];
c = repmat({'\bullet'},size(XYZ,1),1);
V = repmat([53 63 46 3 3 3 27 38 18],size(XYZ,1),1);
case 6
[x y z t1 t2 t3 c] = ...
textread(fname,'%f %f %f %f %f %f %q');
XYZ = [x y z];
t = [t1 t2 t3];
V = repmat([53 63 46 3 3 3 27 38 18],size(XYZ,1),1);
case 15
[x y z t1 t2 t3 c dim1 dim2 dim3 vox1 vox2 vox3 org1 org2 org3] = ...
textread(fname,'%f %f %f %f %f %f %q %f %f %f %f %f %f %f %f %f');
XYZ = [x y z];
t = [t1 t2 t3];
V = [dim1 dim2 dim3 vox1 vox2 vox3 org1 org2 org3];
otherwise
fprintf('Could not read file in its current format')
return
end
pthandles = mip_maker('d',XYZ,t,c,V);
%-----------------------------------------------------------------------
case {'display', 'd'}
switch nargin
case 5
XYZ = varargin{2};
t = varargin{3};
c = varargin{4};
V = varargin{5};
case 4
XYZ = varargin{2};
t = varargin{3};
c = varargin{4};
V = repmat([53 63 46 3 3 3 27 38 18],size(XYZ,1),1);
case 3
XYZ = varargin{2};
t = varargin{3};
c = repmat({'\bullet'},size(XYZ,1),1);
V = repmat([53 63 46 3 3 3 27 38 18],size(XYZ,1),1);
case 2
XYZ = varargin{2};
t = repmat([1 0 0],size(XYZ,1),1);
c = repmat({'\bullet'},size(XYZ,1),1);
V = repmat([53 63 46 3 3 3 27 38 18],size(XYZ,1),1);
otherwise
error('Insufficient parameters to plot points.')
end
pthandles = [];
% set up transformation matrix for points
%------------------------------------------------------------------
for i = 1:size(XYZ,1);
M = [ [diag(V(i,4:6)), -(V(i,7:9)'.*V(i,4:6)')]; [zeros(1,3) ,1]];
xyz = [XYZ(i,:)'; 1];
rcp = round(M\xyz);
rcp = max([min([rcp';[V(i,1:3),1]]);[1,1,1,1]])';
xyz = M*rcp;
%-Create point markers
%--------------------------------------------------------------
h1 = text(Po1+xyz(2),Po2+xyz(1),c{i},'Color',t(i,:),...
'Fontsize',Fontsz,'Tag','X1');
h2 = text(Po1+xyz(2),Po3-xyz(3),c{i},'Color',t(i,:),...
'Fontsize',Fontsz,'Tag','X2');
h3 = text(Po4+xyz(1),Po3-xyz(3),c{i},'Color',t(i,:),...
'Fontsize',Fontsz,'Tag','X3');
set(h1,'userdata',{h2,h3,XYZ(i,:)});
set(h2,'userdata',{h1,h3,XYZ(i,:)});
set(h3,'userdata',{h1,h2,XYZ(i,:)});
pthandles = [pthandles; [h1, h2, h3]];
% By storing the handles in each point the next step will be to
% create a uicontextmenu allowing one to change the point marker
% point size and point color.
end
otherwise
fprintf('Ran out of switch choices\n\n')
end
function MIPfilename = selectMIPFile
[fname pname] = uigetfile('*.mat','Please select the SPM MIP.mat file');
if all(fname == 0)
error('No MIP.mat file found. MIP_MAKER cannot plot points without a plotting grid.')
else
MIPfilename = fullfile(pname,fname);
end
}}}
{{{
function mkdcmclean(fname)
% MKDCMCLEAN removes the calculated fields from a DCM file
% MKDCMCLEAN(FNAME) processes the DCM file specified by FNAME. If FNAME
% is not provided the user is asked to choose one.
% Author: Darren Gitelman
% $Id: mkdcmclean.m,v 1.0 2006-08-30 02:02:06-05 drg Exp drg $
if nargin == 0
switch lower(spm('ver'))
case 'spm2'
fname = spm_get(Inf,'DCM*','Select a DCM file to clean.');
case 'spm5'
fname = spm_select(Inf,'.*DCM.*mat');
end
if isempty(fname)
return
end
end
% fields to remove
remvflds= {'M';'Ep';'Cp';'A';'B';'C';'pA';'pB';'pC';'vA';...
'vB';'vC';'H1';'H2';'K1';'K2';'R';'y';'T';'Ce';'F';'AIC';'BIC'};
for i = 1:size(fname,1)
[pth fn ext] = fileparts(deblank(fname(i,:)));
newfn = [fn,'_clean'];
load(deblank(fname(i,:)))
try
tmp = rmfield(DCM,remvflds);
catch
if numel(fieldnames(tmp)) ~=8
error('Non-standard DCM file')
end
end
DCM = tmp;
save(fullfile(pth,[newfn,ext]),'DCM')
end
}}}
{{{
function roival = mksphroi(varargin)
% MKSPHROI makes a spherical ROI and applies it to an image.
% ROIVAL = MKSPHROI(IMGFILENAME,SAVEFILENAME,[ROI-CENTER],[ROI-RADIUS],ROIIMGFILENAME,)
% creates a spherical ROI of the specified radius, applies it to
% IMAGEFILENAME, saves it to SAVEFILENAME and returns the data in the
% ROIVAL structure. An image of the ROI locations overlaid on
% IMGFILENAME is saved in ROIIMGFILENAME. Or to just enter the center and
% radius of the ROI use MKSPHROI('','',[x y z],r) with x, y, z and r
% replaced by appropriate numbers.
% ROIVAL fields
% imgname Name of image ROI applied to
% roisavfile Name of file that ROI data is saved to.
% roideffile 'point' or the name of a selected roi file.
% roiimgfile Name of image file with ROIs overlaid
% roidat root of ROI data structure (n x 1)
% roicent Center of ROI (1 x 3)
% roirad Radius of ROI (1 x 1)
% allval All values in the ROI. (n x 1)
% meanval Average of all values in the ROI. (1 x 1)
%
% ROIVAL = MKSPHROI(IMGFILENAME,SAVEFILENAME,ROIFILENAME,ROIIMGFILENAME)
% allows the user to select a ROI file. The file should be tab delimited
% and consist of 4 colums, X,Y and Z locations defining the center of
% each ROI and a sphere radius. An image is save with the ROI's overlaid
% on it.
%
% ROIVAL = MKSPHROI(IMGFILENAME,SAVEFILENAME,ROIFILENAME) allows the
% user to select a ROI file. The file should be tab delimited
% and consists of 4 colums, X,Y and Z locations defining the center of
% each ROI and a sphere radius. The user is prompted for an optional
% ROIIMGFILENAME
%
% ROIVAL = MKSPHROI(IMGFILENAME,SAVEFILENAME) prompts the user for the
% ROI filename.
%
% ROIVAL = MKSPHROI(IMGFILENAME) prompts the user for both the filename
% for saving the data and the ROI definition file.
%
% ROIVAL = MKSPHROI() prompts the user for all parameters.
%
% The ROI file should be tab-delimited and be organized as, e.g.
% X Y Z R
% 57 3 48 8
% Author: Darren Gitelman
% $Id: mksphroi.m,v 1.3 2006-12-12 15:16:16-06 drg Exp drg $
imgfile = [];
roisavfile = [];
roideffile = [];
roiimgfile = [];
roicent = [];
roirad = [];
spmver = lower(spm('ver'));
roival = struct(...
'imgfile', [],...
'roisavfile', [],...
'roideffile', [],...
'roiimgfile', [],...
'roidat', struct('roicent', [],...
'roirad', [],...
'allval', [],...
'meanval', []));
% Check the inputs
switch nargin
% First up is image_filename, roi-center, roi-radius
case 5
imgfile = varargin{1};
roisavfile = varargin{2};
roicent = varargin{3};
roirad = varargin{4};
roiimgfile = varargin{5};
% if ~ischar(img) || ~ischar(savefname) || ~ischar(roisavfn) ...
% ~all(size(roicent) == [1 3]) || ~isnumeric(roirad)
% error('Check file names and/or ROI parameters.')
% end
case 4
imgfile = varargin{1};
roisavfile = varargin{2};
if isnumeric(varargin{3})
roicent = varargin{3};
roirad = varargin{4};
else
roideffile = varargin{3};
roiimgfile = varargin{4};
end
case 3
imgfile = varargin{1};
roisavfile = varargin{2};
roideffile = varargin{3};
case 2
imgfile = varargin{1};
roisavfile = varargin{2};
case 1
imgfile = varargin{1};
case 0
% falls through
otherwise
error('Check data inputs.')
end
if isempty(imgfile)
switch spmver
case 'spm2'
imgfile = spm_get(1,'image','Select an image file.');
case 'spm5'
imgfile = spm_select(1,'image','Select an image file.');
otherwise
error('Unknown version of SPM.')
end
drawnow;
end
if isempty(roicent) && isempty(roideffile)
switch spmver
case 'spm2'
roideffile = spm_get(1,'*','Select a ROI file.');
case 'spm5'
roideffile = spm_select(1,'*','Select a ROI file.');
otherwise
error('Unknown version of SPM.')
end
drawnow;
elseif ~isempty(roicent)
roideffile = 'point';
end
if isempty(roisavfile)
[roisavfn roisavpth]=uiputfile('*.mat','File name to save ROI data as...');
roisavfile = fullfile(roisavpth,roisavfn);
end
if isempty(roiimgfile)
[roiimgfn roiimgpth]=uiputfile('*.img','File name to save image+ROI as...');
roiimgfile = fullfile(roiimgpth,roiimgfn);
end
% if isempty(imgfile) || isempty(roisavfile) || (imgfname == 0) || (roifname == 0)
% return
% end
drawnow
% save the filenames in the structure
roival.imgfile = imgfile;
roival.roisavfile = roisavfile;
roival.roideffile = roideffile;
roival.roiimgfile = roiimgfile;
% map then read the volume
vimg = spm_vol(imgfile);
img = spm_read_vols(vimg);
% volume to save
savimg = img;
savimg = savimg/max(max(max(savimg)));
% get the rois if not a point.
if isempty(roicent)
roitmp = load(roideffile);
roicent = roitmp(:,1:3);
roirad = roitmp(:,4);
end
for i = 1:numel(roirad)
mc = roicent(i,:);
mr = roirad(i);
% find voxels that are within sphere
vox = sqrt(sum(vimg.mat(1:3,1:3)'.^2));
vr = mr ./ vox;
vc = vimg.mat \ [mc(1:3) 1]';
vc = vc(1:3)';
blim = [max([1 1 1; ceil(vc-vr)]); min([vimg.dim(1:3); floor(vc+vr)])];
[R,C,P]=ndgrid(...
blim(1,1):blim(2,1),blim(1,2):blim(2,2),blim(1,3):blim(2,3));
vXYZ = [R(:)';C(:)';P(:)'];
o = ones(1, size(vXYZ, 2));
pts = find(sqrt(sum(((vXYZ-(vc'*o)) .* (vox'*o)).^2)) <= mr);
pts = vXYZ(:,pts);
vals = ones(1, size(pts, 2));
dinds = pts(1,:) + ...
(pts(2,:)-1) * vimg.dim(1) + ...
(pts(3,:)-1) * vimg.dim(1)*vimg.dim(2);
savimg(dinds) = savimg(dinds)+ i;
roival.roidat(i).allval = img(dinds);
roival.roidat(i).meanval = mean(roival.roidat(i).allval);
end
save(roisavfile,'roival');
vroiimg = vimg;
vroiimg.fname = roiimgfile;
spm_write_vol(vroiimg,savimg);
}}}
remove detritus from the matlabpath
{{{
function nbil_cleanpath(pattern,verbose)
% NBIL_CLEANPATH removes entries from the matlabpath containing pattern
% NBIL_CLEANPATH(PATTERN,VERBOSE) removes path entries containing the string
% in PATTERN. If VERBOSE is 0 or not given no feedback is provided. If
% VERBOSE is 1, the user is told if the pattern is not found or the
% entries removed are listed.
%
% NOTE: The program makes no attempt to be specific in its operation. It
% removes lines with any matches to pattern.
% Example:
% nbil_cleanpath('work') will remove
% /mydisk/work
% /mydisk/my_work, etc.
% Author: Darren Gitelman
% $Id: nbil_cleanpath.m,v 1.0 2007-08-23 20:28:30-05 drg Exp drg $
switch nargin
case 0
error('Not enough input arguments');
case 1
verbose = 0;
end
inpath = path;
patIdx = strfind(inpath,pattern);
if isempty(patIdx)
if verbose
fprintf('%s not found in path.\n',pattern);
end
return
end
% path delimiter for matlab is ;
% somewhat annoyingly, however, Matlab does not terminate the path string
% with a semicolon
inpath = [inpath,';'];
scIdx = strfind(inpath,';');
scIdx = [0, scIdx];
pathsToRem = {};
% the index of entries containing the pattern to be removed is iteratively
% shortened. We exit when there are no more entries to remove.
for i = 2:numel(scIdx)
if ~isempty(patIdx)
x = patIdx < scIdx(i);
if ~isempty(find(x,1))
pathsToRem{end+1} = inpath(scIdx(i-1)+1:scIdx(i)-1);
patIdx(x) = [];
end
else
break
end
end
for i = 1:numel(pathsToRem)
try
rmpath(pathsToRem{i})
if verbose
fprintf('Removing path entry: %s\n',pathsToRem{i});
end
catch
if verbose
fprintf('Could not remove path entry: %s\n',pathsToRem{i});
end
end
end
}}}
To use, copy all code below and save in the matlab path in a file named nbil_mosaic.m
(Make sure to copy the code as displayed in yellow, don't double-click this tiddler)
{{{
function nbil_mosaic(action)
% NBIL_MOSAIC Displays a mosaic of (optionally) superimposed images
% NBIL_MOSAIC(ACTION) where ACTION is one of
% 'initialize' or not supplied -- initialize all params by user input
% 'draw' -- draw the mosaic with current parameters.
% 'stCoordCB' -- update the starting coordinate
% 'drawROICB' -- turn on or off ROI drawing
% 'updateorCodeCB' -- update the orientation information
% 'updateRowCB' -- update the number of rows
% 'updateColCB' -- update the number of columns
% 'updateIncrementCB' -- update the slice increment
% 'updateDirCB' -- update the direction of drawing
% 'updateCECB' -- update the contrst enhancement choice
% 'updateInterpCB' -- update the interpolation method
%_________________________________________________________
%
% nbil_mosaic is based on spm_sections (spm96) and is used
% to create a mosaic of orthogonal sections though a background image.
% Regional foci from the selected SPM{Z} are rendered on these images
% using colormap(s) specified by user.
%
% Images are drawn in the SPM Graphics figure.
% User is prompted for
% Background data set ( usually a T1 gray scale image volume )
% Optional SPM overlay dataset. (using spm_getSPM)
% Orientation (axial, sagittal, coronal)
% Starting slice position
% Number of rows & cols in the mosaic of images
% Spacing (mm) of slices through the dataset.
% Direction of progresion through dataset (ascending/descending)
% Contrast enhancement of background image (Y/N)
% SPM interpolation choice (nearest neighbor/bilinear)
%
% spm_getSPM is called to prompt user
% for info about the superimposed results from SPM{Z}.
%
% When two datasets are merged, as in spm_sections(), a split colormap
% is used. The activation part of the colormap is scaled into
% the maximum range of the activations in the entire dataset,
% not just the slices being rendered in the current mosaic.
% This allows several mosaics from the same dataset to have
% their colored activation regions to be directly compared.
% However, the gray-scaled background images are individually
% scaled to fit the lower part of the split colormap.
% They can be contrast-enhanced at user request. In contrast enhancement
% the low-end intensities are mapped from 0 to the value which
% corresponds to having accounted for 97% of the total pixels.
% The remaining 3% are mapped to the maximum intensity.
%_________________________________________________________________________
% Authors: Roger Ray, Darren Gitelman
% $Id: nbil_mosaic.m,v 1.6 2008-09-19 09:39:36-05 drg Exp drg $
if nargin == 0
action = 'initialize';
end;
switch lower(action)
case 'initialize'
initMosaic;
case 'draw'
drawMosaic;
case 'stcoordcb'
stCoordCB;
case 'drawroicb'
drawROICB;
case 'updateorcodecb'
updateorCodeCB
case 'updaterowcb'
updateRowCB
case 'updatecolcb'
updateColCB
case 'updateincrementcb'
updateIncrementCB
case 'updatedircb'
updateDirCB
case 'updatececb'
updateCECB
case 'updateinterpcb'
updateInterpCB
case 'lowslicecb'
SliceCB('low')
case 'hislicecb'
SliceCB('high')
otherwise
disp('Action not recognized');
end
return;
%==================================================================================
% INITIALIZE MOSAIC PARAMETERS FROM USER INPUT
%==================================================================================
function initMosaic
%
% Subfunction to initialize parameters governing creation of mosaic
%
% -------------------------------------------------------------------------
ver = lower(spm('ver'));
% Get handles to the interactive & graphics figures
%--------------------------------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
Fgraph = spm_figure('FindWin','Graphics');
spm_clf(Finter);
spm_clf(Fgraph);
%-Get the background (anatomic) image on which activations can be rendered
%--------------------------------------------------------------------------
switch lower(ver)
case 'spm99'
imgfilt = '*.img';
getfile = @spm_get;
case 'spm2'
imgfilt = 'IMAGE';
getfile = @spm_get;
case 'spm5'
imgfilt = 'image';
getfile = @spm_select;
otherwise
imgfilt = 'IMAGE';
getfile = @spm_select;
end
bgfn = getfile(1,imgfilt,'Select anatomical image volume',[]);
Vbg = spm_vol(bgfn);
VbgOrg = -Vbg.mat(1:3,4)'; % Origin (in mm) of bg volume
VbgDim = Vbg.dim(1:3); % Dimensions (in voxels) of bg volume
VbgVox = sqrt(sum(Vbg.mat(1:3,1:3).^2)); % Voxel size (in mm) of bg volume
VbgOrg = round(VbgOrg./VbgVox); % Origin (in voxels) of bg volume
% -------------------------------------------------------------------------
% Select SPM(s) for Overlay
% -------------------------------------------------------------------------
addSPM = 1;
nSPM = 0;
SPMExtras = struct('M', eye(4), 'cmap', []);
line = 1;
while (addSPM && (nSPM < 4))
addSPM = spm_input(['Superimpose SPM ',num2str(nSPM+1),'?'],line,...
'b','no|yes',[0 1],1);
if addSPM
%-----------------------------------------------------------------------
% Get thresholded data, thresholds and parameters
%-----------------------------------------------------------------------
[SPM, SPMVol] = spm_getSPM;
switch ver
case 'spm99'
Z = SPM.Z;
case 'spm2'
Z = SPMVol.Z;
case 'spm5'
Z = SPMVol.Z;
end
if isempty(Z)
resp = spm_input('No SPM data found. Do you want to continue?',line, 'b', 'no|yes',[0 1], 1);
if resp == 0; return; end;
addSPM = 0;
else
nSPM = nSPM + 1;
switch ver
case 'spm99'
SPMList(nSPM) = SPM; %#ok<AGROW>
case 'spm2'
SPMList(nSPM) = SPMVol; %#ok<AGROW>
case 'spm5'
SPMList(nSPM) = SPMVol;
end
SPMExtras(nSPM).M = SPMVol.M;
for line = 1:nSPM
str = sprintf('SPM: %s Ht thresh: %.2f Ext thresh: %.2f vox',...
SPMList(line).title, SPMList(line).u, SPMList(line).k);
h = str2finter(str, line);
set(h, 'HorizontalAlignment', 'Left');
end;
line = line + 1;
end
end;
end;
%----------------------------------------------------------------------------
% Colormap choice for SPMs
%---------------------------------------------------------------------------
if nSPM ==1
SPMExtras(1).cmap = 'hot';
end;
if (nSPM > 1)
choice = -1;
while choice < 0
choice = spm_input('Colormap',line, 'b', 'Default|Other...',[0 1], 0);
if choice == 0
SPMExtras(1).cmap = 'default';
else
cmapfn = getfile([0,1],'.rgb','Select 15-level RGB colormap');
if isempty(cmapfn)
SPMExtras(1).cmap = 'default';
else
cmap = load(cmapfn);
errStr = [];
if (size(cmap,1) ~= 15) || (size(cmap,2) ~= 3)
errStr{1} = 'Colormap must have 15 rows and 3 columns';
choice = -1;
end;
if min(min(cmap)) < 0 || max(max(cmap)) > 1
errStr{2} = 'RGB color values must be between 0 and 1';
choice = -1;
end;
if choice == 1,
SPMExtras(1).cmap = cmap;
else
warndlg(errStr);
end;
end;
end; % endif choice
end; % endwhile choice < 0
end; % endif nSPM
%-----------------------------------------------------------------------------------
% Construct an appropriate 128-level colormap
% dependent on the number of SPMs superimposed on the background image.
% nSPM = 0: 128-level gray map
% nSPM = 1: 64-level gray map + 64-level "hot" map (cf. spm_figure.m)
% nSPM > 1: 92-level gray map + 15-level overlap map (SPMExtras.cmap or default)
% 15-level overlap map
% Slots 1, 2, 4, 8 <=> Activations 1, 2, 3, 4 alone
% All others are arithmetic combinations of the primary slot values
% e.g. slot 5 (=1+4) <=> overlap of Activations 1 & 3
% e.g. slot 11 (=1+2+8) <=> overlap of Activations 1, 2 & 4
%-----------------------------------------------------------------------------------
cmapSize = 128;
switch nSPM
case 0
nGrays = cmapSize;
segSize = 0;
cmap = gray(nGrays);
case 1
% -----------------------------------------------------
% Use the SPM99 "gray-hot" map. (See spm_figure.m)
% The hot section is shifted up from the dark end
% of the standard Matlab hot map.
% -----------------------------------------------------
nGrays = 64;
segSize = cmapSize - nGrays;
hotmap = hot(segSize+16); hotmap = hotmap(1:segSize + 16,:);
cmap = [gray(nGrays); hotmap];
otherwise
% -----------------------------------------------------
% Use a 15-level color map.
% -----------------------------------------------------
nGrays = cmapSize - 16;
segSize = 1;
if strcmp(SPMExtras(1).cmap, 'default')
dfltcmap = nbil_defaultcmap;
cmap = [gray(nGrays); dfltcmap];
else
cmap = [gray(nGrays); SPMExtras(1).cmap];
end;
end; % switch(nSPM)
%----------------------------------------------------------------------------
% Set Vol struct according to optional SPM overlay
%---------------------------------------------------------------------------
if (nSPM > 0)
Vol = struct('name', 'Functional',...
'org', SPMVol.iM(1:3,4)',...
'dim', SPMVol.DIM,...
'vox', SPMVol.VOX');
else
Vol = struct('name', 'Anatomic',...
'org', VbgOrg,...
'dim', VbgDim',...
'vox', VbgVox);
end;
% Set up always ON GUI
% ---------------------
% ---------------------
% Orientation
% ---------------------
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
% Prompt Text
uicontrol(Finter,'Style','Text',...
'String','Select an Orientation:',...
'HorizontalAlignment','Right',...
'Position',PRec);
% Buttons
buttonWidth = (RRec(3) - 10)/3;
uicontrol(Finter,'Style','PushButton',...
'String','Sagittal',...
'Position',[RRec(1) RRec(2) buttonWidth RRec(4)],...
'Callback','nbil_mosaic(''updateorCodeCB'')');
uicontrol(Finter,'Style','PushButton',...
'String','Coronal',...
'Position',[RRec(1)+buttonWidth+5 RRec(2) buttonWidth RRec(4)],...
'Callback','nbil_mosaic(''updateorCodeCB'')');
uicontrol(Finter,'Style','PushButton',...
'String','Axial',...
'Position',[RRec(1)+(2*buttonWidth)+10 RRec(2) buttonWidth RRec(4)],...
'Callback','nbil_mosaic(''updateorCodeCB'')');
orCodeLine = line;
% choose an initial orCode of axial. This way the rest of the buttons can
% be drawn.
orCode = 3;
% Show axial slice info
line = line + 1;
orientStr = ['Sagittal'; 'Coronal '; 'Axial '];
str = sprintf('%s %s Slices: %d ', deblank(orientStr(orCode,:)),...
Vol.name, Vol.dim(orCode));
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
uicontrol(Finter,'Style','Text',...
'String',str,...
'HorizontalAlignment','Right',...
'Position',PRec,...
'Tag','SLICES');
% orignal slice spacing
str = sprintf('Spacing: %0.2g mm', Vol.vox(orCode));
uicontrol(Finter,'Style','Text',...
'String',str,...
'HorizontalAlignment','Left',...
'Position',RRec,...
'Tag','SPACING');
% show number of total slices and spacing on the next line
line = line + 1;
str = sprintf('Starting Coord (%0.2f to %0.2f mm):',-Vol.org(orCode)*Vol.vox(orCode),...
(Vol.dim(orCode)-Vol.org(orCode))*Vol.vox(orCode));
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
uicontrol(Finter,'Style','Text',...
'String',str,...
'HorizontalAlignment','Right',...
'Position',PRec,...
'Tag','STARTCOORD');
%----------------------------------------------------------------------------
% start coord edit box.
%----------------------------------------------------------------------------
lineStCoord = line;
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
wid = round(RRec(3)/3);
RRec(1) = RRec(1)+wid+5;
RRec(3) = wid-10;
uicontrol(Finter, ...
'Style', 'edit',...
'BackgroundColor',[1 1 1],...
'HorizontalAlignment', 'center',...
'Position', RRec,...
'String', '',...
'Tag', 'nbilmosaicStCoord',...
'Callback', 'nbil_mosaic(''stCoordCB'')');
%----------------------------------------------------------------------------
% higher and lower slice UIControls
%----------------------------------------------------------------------------
RRec(1) = RRec(1)-wid-5;
uicontrol(Finter,...
'Style','pushbutton',...
'HorizontalAlignment','center',...
'Position',RRec,...
'String', '<',...
'Tag','nbilmosaicLowerSlice',...
'Callback','nbil_mosaic(''lowslicecb'')');
RRec(1) = RRec(1)+5+wid+5+wid;
uicontrol(Finter,...
'Style','pushbutton',...
'HorizontalAlignment','center',...
'Position',RRec,...
'String', '>',...
'Tag','nbilmosaicLowerSlice',...
'Callback','nbil_mosaic(''hislicecb'')');
startCoord = [];
line = line+1.3;
%----------------------------------------------------------------------------
% Row and Column popup menus
%----------------------------------------------------------------------------
maxRow = 5;
maxCol = 4;
WIN = get(Fgraph,'Position');
aspect = WIN(3)/WIN(4);
maxCol = fix(maxRow*aspect);
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
wid = round(RRec(3)/3);
RRec(1) = RRec(1)+wid;
RRec(3) = wid;
uicontrol(Finter,'Style','Text',...
'String','Rows in mosaic: ',...
'HorizontalAlignment','Right',...
'Position',PRec);
h = uicontrol(Finter,'Style','PopUp',...
'String','1|2|3|4|5|6|7|8',...
'Position',RRec,...
'horizontalalignment','center',...
'Tag','POPMAXROW',...
'Callback','nbil_mosaic(''updateRowCB'')');
set(h,'Value',maxRow)
line = line + 1.3;
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
wid = round(RRec(3)/3);
RRec(1) = RRec(1)+wid;
RRec(3) = wid;
uicontrol(Finter,'Style','Text',...
'String','Columns in mosaic: ',...
'HorizontalAlignment','Right',...
'Position',PRec);
h = uicontrol(Finter,'Style','PopUp',...
'String','1|2|3|4|5|6|7|8',...
'Position',RRec,...
'horizontalalignment','center',...
'Tag','POPMAXCOL',...
'Callback','nbil_mosaic(''updateColCB'')');
set(h,'Value',maxCol);
line = line+1.4;
%----------------------------------------------------------------------------
% Spacing of tiles in mosaic
% Adjusted to be a multiple of func data spacing.
%----------------------------------------------------------------------------
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
str1 = num2str(Vol.vox(orCode));
str2 = num2str(2*Vol.vox(orCode));
str3 = 'Other...';
spacingLine = line;
buttonWidth = (RRec(3)-10)/3;
uicontrol(Finter,'Style','pushbutton',...
'String',str1,...
'Position',[RRec(1) RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHPOS1',...
'Callback','nbil_mosaic(''updateIncrementCB'')');
uicontrol(Finter,'Style','pushbutton',...
'String',str2,...
'Position',[RRec(1)+buttonWidth+5 RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHPOS2',...
'Callback','nbil_mosaic(''updateIncrementCB'')');
uicontrol(Finter,'Style','pushbutton',...
'String',str3,...
'Position',[RRec(1)+(2*buttonWidth)+10 RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHPOS3',...
'Callback','nbil_mosaic(''updateIncrementCB'')');
% Initial spacing is the slice separation in the axial direction
spacing = Vol.vox(orCode);
% prompt string
prompt = sprintf('%s Slice Increment (mm): %0.1f ', deblank(orientStr(orCode,:)),spacing);
uicontrol(Finter,'Style','Text',...
'String',prompt,...
'HorizontalAlignment','right',...
'Position',PRec,...
'Tag','TEXTINCREMENT');
line = line + 1;
%----------------------------------------------------------------------------
% Direction of mosaic tile progression
%----------------------------------------------------------------------------
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
% choose ascending progression and update later
uicontrol(Finter,'Style','Text',...
'String','Mosaic Tile Progression: Ascend',...
'HorizontalAlignment','right',...
'Position',PRec,...
'Tag','TEXTPROGRESSION');
buttonWidth = (RRec(3)-10)/2;
uicontrol(Finter,'Style','pushbutton',...
'String','Ascending',...
'Position',[RRec(1) RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHASCEND',...
'Callback','nbil_mosaic(''updateDirCB'')');
uicontrol(Finter,'Style','pushbutton',...
'String','Descending',...
'Position',[RRec(1)+buttonWidth+10 RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHDESCEND',...
'Callback','nbil_mosaic(''updateDirCB'')');
direction = 1;
line = line + 1;
% --------------------------------------------------------------------------
% Contrast enhancement choice
%----------------------------------------------------------------------------
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
% Choose contrast enhancement
uicontrol(Finter,'Style','Text',...
'String','Contrast Enhance Background: No',...
'HorizontalAlignment','right',...
'Position',PRec,...
'Tag','TEXTCE');
buttonWidth = (RRec(3)-10)/2;
uicontrol(Finter,'Style','pushbutton',...
'String','Yes',...
'Position',[RRec(1) RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHCEYES',...
'Callback','nbil_mosaic(''updateCECB'')');
uicontrol(Finter,'Style','pushbutton',...
'String','No',...
'Position',[RRec(1)+buttonWidth+10 RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHCENO',...
'Callback','nbil_mosaic(''updateCECB'')');
contrastEnhance = 0;
line = line + 1;
% --------------------------------------------------------------------------
% SPM interpolation choice
%----------------------------------------------------------------------------
if (nSPM > 0)
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
uicontrol(Finter,'Style','Text',...
'String','Interpolation Type: Point',...
'HorizontalAlignment','right',...
'Position',PRec,...
'Tag','TEXTINTERP');
buttonWidth = (RRec(3)-10)/2;
uicontrol(Finter,'Style','pushbutton',...
'String','Point',...
'Position',[RRec(1) RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHINTERPPOINT',...
'Callback','nbil_mosaic(''updateInterpCB'')');
uicontrol(Finter,'Style','pushbutton',...
'String','Bilinear',...
'Position',[RRec(1)+buttonWidth+10 RRec(2) buttonWidth RRec(4)],...
'Tag','PUSHINTERPBILINEAR',...
'Callback','nbil_mosaic(''updateInterpCB'')');
SPMinterp = 0;
line = line + 1;
end
% --------------------------------------------------------------------------
% Draw push button. Use must press to redraw mosaic
%----------------------------------------------------------------------------
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
uicontrol(Finter,'Style','pushbutton',...
'String','Draw',...
'Position',RRec,...
'CallBack','nbil_mosaic(''draw'')');
line = line + 1;
% ------------------------------------------------------------------------
% Create transformation matrix, A, and bounding box (mm), bbox,
% of the background volume (cf spm_orthviews internal fn maxbb()).
% ------------------------------------------------------------------------
A = Vbg.mat;
bb = [[1 1 1]; Vbg.dim(1:3)];
c = [ bb(1,1) bb(1,2) bb(1,3) 1
bb(1,1) bb(1,2) bb(2,3) 1
bb(1,1) bb(2,2) bb(1,3) 1
bb(1,1) bb(2,2) bb(2,3) 1
bb(2,1) bb(1,2) bb(1,3) 1
bb(2,1) bb(1,2) bb(2,3) 1
bb(2,1) bb(2,2) bb(1,3) 1
bb(2,1) bb(2,2) bb(2,3) 1]';
c = Vbg.mat*c;
c = c(1:3,:)';
bbox = [min(c); max(c)];
% -----------------------------------------------------------------
% The checkbox for enabling "Draw ROI"
% -----------------------------------------------------------------
hDrawROIPrompt = [];
if (nSPM > 0) && (exist('nbil_mosaicROI') == 2)
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line, '', Finter);
uicontrol(Finter,'Style','checkbox',...
'Position',RRec,...
'HorizontalAlignment','left', ...
'String','Enable ROI Calculation',...
'Value', 0,...
'tooltipstring','Calculate area of ROI on mosaic images',...
'CallBack', 'nbil_mosaic(''drawROICB'')');
% line = line+1 ;
hDrawROIPrompt(1)= uicontrol(Finter,'Style','Text',...
'String','Select image tile with left mouse button',...
'HorizontalAlignment','left',...
'Position',[PRec(1) PRec(2)+3 PRec(3) PRec(4)],...
'Visible','off');
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', line+1, '', Finter);
hDrawROIPrompt(2) = uicontrol(Finter,'Style','Text',...
'String','Left button picks ROI contour points. Right button picks last pt.',...
'HorizontalAlignment','left',...
'Position',[PRec(1) PRec(2)+6 PRec(3)+RRec(3) PRec(4)],...
'Visible','off');
end;
% nbil_mosaic structure
% ----------------------------------
nbilmosaic = struct ('Vbg', Vbg,...
'VbgOrg', VbgOrg,...
'VbgDim', VbgDim,...
'VbgVox', VbgVox,...
'bbox', bbox,...
'nSPM', nSPM,...
'SPM', [],...
'SPMExtras', [],...
'SPMinterp', [],...
'Vol', Vol,...
'A', A,...
'orCode', orCode,...
'startCoord', startCoord',...
'cmap', cmap,...
'nGrays', nGrays,...
'segSize', segSize,...
'contrastEnhance', contrastEnhance,...
'maxRow', maxRow,...
'maxCol', maxCol,...
'drawROI', 0,...
'hDrawROIPrompt', [],...
'spacing', spacing,...
'direction',direction,...
'UI',struct('orCodeLine',orCodeLine,...
'lineStCoord',lineStCoord,...
'spacingLine',spacingLine,...
'GUIinput',[]));
if (nSPM > 0)
nbilmosaic.SPMVol = SPMList;
nbilmosaic.SPMExtras = SPMExtras;
nbilmosaic.SPMinterp = SPMinterp;
nbilmosaic.drawROI = 0;
nbilmosaic.hDrawROIPrompt = hDrawROIPrompt;
end;
% -----------------------------------------------
% Create a dummy, invisible uicontrol for Finter
% and attach the nbilmosaic struct as UserData
% -----------------------------------------------
h = uicontrol(Finter, ...
'Style', 'Text',...
'Tag','nbilmosaic',...
'UserData', nbilmosaic,...
'Visible', 'off');
return; % END FUNCTION INITMOSAIC
%=========================================================================
% UPDATE orCode
%=========================================================================
function updateorCodeCB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
hnbil = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(hnbil); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(hnbil, 'UserData');
% Sagittal=1, Coronal=2, Axial=3
orString = get(gcbo,'String');
switch orString
case 'Sagittal'
nbilmosaic.orCode = 1;
case 'Coronal'
nbilmosaic.orCode = 2;
case 'Axial'
nbilmosaic.orCode = 3;
end
% Show the slices
orientStr = ['Sagittal'; 'Coronal '; 'Axial '];
str = sprintf('%s %s Slices: %d ', deblank(orientStr(nbilmosaic.orCode,:)),...
nbilmosaic.Vol.name, nbilmosaic.Vol.dim(nbilmosaic.orCode));
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', nbilmosaic.UI.orCodeLine, '', Finter);
h = findobj(Finter,'Tag','SLICES');
set(h,'String',str)
% Revise the prompt string
str = sprintf('Spacing: %0.3g mm', nbilmosaic.Vol.vox(nbilmosaic.orCode));
h = findobj(Finter,'Tag','SPACING');
set(h,'String',str)
% show the slice positions on the next line
str = sprintf('Starting Coord (%0.2f to %0.2f mm):',...
-nbilmosaic.Vol.org(nbilmosaic.orCode)*nbilmosaic.Vol.vox(nbilmosaic.orCode),...
(nbilmosaic.Vol.dim(nbilmosaic.orCode)-nbilmosaic.Vol.org(nbilmosaic.orCode))*...
nbilmosaic.Vol.vox(nbilmosaic.orCode));
h = findobj(Finter,'String','STARTCOORD');
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', nbilmosaic.UI.lineStCoord, '', Finter);
set(h,'String',str)
% update the slice increment buttons
str1 = sprintf('%0.3f',nbilmosaic.Vol.vox(nbilmosaic.orCode));
str2 = sprintf('%0.3f',2*nbilmosaic.Vol.vox(nbilmosaic.orCode));
str3 = 'Other...';
set(findobj('Tag','PUSHPOS1'),'String',str1)
set(findobj('Tag','PUSHPOS2'),'String',str2)
set(findobj('Tag','PUSHPOS3'),'String',str3,'Style','Pushbutton')
nbilmosaic.spacing = nbilmosaic.Vol.vox(nbilmosaic.orCode);
% Update the slice increment prompt
switch nbilmosaic.orCode
case 1
orient = 'Sagittal';
case 2
orient = 'Coronal';
case 3
orient = 'Axial';
end
prompt = sprintf('%s Slice Increment (mm): %0.3f ',...
orient,nbilmosaic.spacing*nbilmosaic.direction);
set(findobj('Tag','TEXTINCREMENT'),'String',prompt)
% set the edit box to empty
h = findobj(Finter,'Tag','nbilmosaicStCoord');
set(h,'String','')
nbilmosaic.startCoord = [];
set(hnbil,'UserData',nbilmosaic)
return % END UPDATEORCODE
%=========================================================================
% UPDATE ROW POPUPMENU
%=========================================================================
function updateRowCB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
% Get the row value
hRow = findobj('Tag','POPMAXROW');
maxRow = get(hRow,'Value');
nbilmosaic.maxRow = maxRow;
% save nbilmosaic
set(h,'UserData',nbilmosaic);
return % END UPDATEROWCB
%=========================================================================
% UPDATE COL POPMENU
%=========================================================================
function updateColCB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
% Get the row value
hCol = findobj('Tag','POPMAXCOL');
maxCol = get(hCol,'Value');
nbilmosaic.maxCol = maxCol;
% save nbilmosaic
set(h,'UserData',nbilmosaic);
return % END UPDATECOLCB
%=========================================================================
% UPDATE INCREMENT
%=========================================================================
function updateIncrementCB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
hGCBO = gcbo;
spaceobj = get(hGCBO,'Tag');
% Get the selected button
switch spaceobj
case {'PUSHPOS1','PUSHPOS2'}
nbilmosaic.spacing = str2num(get(hGCBO,'String'));
hpos3 = findobj('Tag','PUSHPOS3');
set(hpos3,'Style','pushbutton','String','Other...','BackgroundColor',[0.7 0.7 0.7])
case 'PUSHPOS3'
% if the Other button was selected then change the button to an
% edit box for user entry. The edit box is changed back to a button
% if the user subsequently selects another spacing increment
hpos3 = findobj('Tag','PUSHPOS3');
if strcmp(get(hpos3,'Style'),'pushbutton')
set(hpos3,'Style','edit','String','','BackgroundColor',[1 1 1])
else
spacenum = str2num(get(hpos3,'String'));
if ~isempty(spacenum)
nbilmosaic.spacing = spacenum;
end
end
end
% Update the slice increment prompt
switch nbilmosaic.orCode
case 1
orient = 'Sagittal';
case 2
orient = 'Coronal';
case 3
orient = 'Axial';
end
prompt = sprintf('%s Slice Increment (mm): %0.3f ',...
orient,nbilmosaic.spacing*nbilmosaic.direction);
set(findobj('Tag','TEXTINCREMENT'),'String',prompt)
set(h,'UserData',nbilmosaic)
return % END UPDATEINCREMENTCB
%=========================================================================
% UPDATE DIRECTION
%=========================================================================
function updateDirCB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
% Get the progression value
hDir = gcbo;
if strcmp(get(hDir,'String'),'Ascending')
direction = 1;
dirstr = 'Ascend';
else
direction = -1;
dirstr = 'Descend';
end
nbilmosaic.direction = direction;
% Update the prompt
prompt = sprintf('Mosaic Tile Progression: %s',dirstr);
set(findobj('Tag','TEXTPROGRESSION'),'String',prompt)
switch nbilmosaic.orCode
case 1
orient = 'Sagittal';
case 2
orient = 'Coronal';
case 3
orient = 'Axial';
end
prompt = sprintf('%s Slice Increment (mm): %0.3f ',...
orient,nbilmosaic.spacing*nbilmosaic.direction);
set(findobj('Tag','TEXTINCREMENT'),'String',prompt)
% save nbilmosaic
set(h,'UserData',nbilmosaic);
return % END UPDATEDIRCB
%=========================================================================
% UPDATE CONTRAST ENHANCEMENT
%=========================================================================
function updateCECB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
% Get the CE value
hCE = gcbo;
if strcmp(get(hCE,'String'),'Yes')
contrastEnhance = 1;
cestr = 'Yes';
else
contrastEnhance = 0;
cestr = 'No';
end
nbilmosaic.contrastEnhance = contrastEnhance;
% Update the prompt
prompt = sprintf('Contrast Enhance Background: %s',cestr);
set(findobj('Tag','TEXTCE'),'String',prompt)
% save nbilmosaic
set(h,'UserData',nbilmosaic);
return % END UPDATECECB
%=========================================================================
% UPDATE INTERPOLATION
%=========================================================================
function updateInterpCB
% Get the Mosaic Structure
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
% Get the Interp value
hInterp = gcbo;
if strcmp(get(hInterp,'String'),'Point')
SPMinterp = 0;
interpstr = 'Point';
else
SPMinterp = 1;
interpstr = 'Bilinear';
end
nbilmosaic.SPMinterp = SPMinterp;
% update the prompt
prompt = sprintf('Interpolation Type: %s',interpstr);
set(findobj('Tag','TEXTINTERP'),'String',prompt)
% save nbilmosaic
set(h,'UserData',nbilmosaic);
return % END UPDATEINTERPCB
%=========================================================================
% DRAW MOSAIC
%=========================================================================
function drawMosaic
% ------------------------------------------------------
%
% Internal subfunction drawMosaic
%
% ------------------------------------------------------
% ------------------------------------------------------
% Retrieve the handle to the UI start coord control
% which contains the UserData to draw the mosaic.
% ------------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% -----------------------------------------
% Retrieve the stored values from UserData.
% -----------------------------------------
nbilmosaic = get(h, 'UserData');
Vbg = nbilmosaic.Vbg;
VbgOrg = nbilmosaic.VbgOrg;
VbgDim = nbilmosaic.VbgDim;
VbgVox = nbilmosaic.VbgVox;
bbox = nbilmosaic.bbox;
Vol = nbilmosaic.Vol;
A = nbilmosaic.A;
orCode = nbilmosaic.orCode;
startCoord = nbilmosaic.startCoord;
cmap = nbilmosaic.cmap;
nGrays = nbilmosaic.nGrays;
segSize = nbilmosaic.segSize;
spacing = nbilmosaic.spacing * nbilmosaic.direction;
contrastEnhance = nbilmosaic.contrastEnhance;
maxRow = nbilmosaic.maxRow;
maxCol = nbilmosaic.maxCol;
if isempty(Vbg) || isempty(Vol) || isempty(orCode) ||...
isempty(startCoord) || isempty(spacing) ||...
isempty(contrastEnhance) ||isempty(maxRow) || isempty(maxCol)
return
end
% ---------------------------------
% Retrieve SPM(s), if supplied.
% ---------------------------------
nSPM = nbilmosaic.nSPM;
if (nSPM == 0)
SPMVol = [];
SPMExtras = [];
else
SPMVol = nbilmosaic.SPMVol;
SPMExtras = nbilmosaic.SPMExtras;
SPMinterp = nbilmosaic.SPMinterp;
end
startMin = -Vol.org(orCode)*Vol.vox(orCode);
startMax = (Vol.dim(orCode)-Vol.org(orCode))*Vol.vox(orCode);
if startCoord < startMin; startCoord = startMin; end;
if startCoord > startMax; startCoord = startMax; end;
TalL = zeros(1,3);
TalL(orCode) = startCoord;
nImgs = maxRow*maxCol;
%----------------------------------------------------------------------------
% Get handle to the graphics figure & clear it of all old objects
%----------------------------------------------------------------------------
Fgraph = spm_figure('FindWin','Graphics');
spm_clf(Fgraph);
set([Finter,Fgraph],'Pointer','Watch');
%----------------------------------------------------------------------------
% delete previous axis
%----------------------------------------------------------------------------
figure(Fgraph)
subplot(2,1,2);
delete(gca);
spm_figure('DeletePageControls');
spm_figure('Clear');
%-----------------------------------------------------------------------------------
% Set the Fgraph figure colormap
%-----------------------------------------------------------------------------------
% if bgWhite
% cmap(1,:) = [1 1 1]; % set first color slot (bg color) to white
% end
colormap(cmap);
%----------------------------------------------------------------------------
% compute axes to correct for anisotropy of voxels and (normalized) window
%----------------------------------------------------------------------------
set(Fgraph,'Units','pixels');
WIN = get(gcf,'Position');
fHoriz = 2/WIN(3); % Horizontal spacing (pixels) is numerator.
fVert = 18/WIN(4); % Vertical spacing (pixels) is numerator.
top = 1 - (32/WIN(4)); % Top is just below Graphics menu bar (~32 pixels)
xLbl = ['x = '; 'y = '; 'z = '];
%----------------------------------------------------------------------------
% width & height of mosaic tiles, taking into account gaps between tiles
%----------------------------------------------------------------------------
w = (1 - fHoriz*(maxCol-1))/maxCol;
h = (top - fVert*(maxRow+6))/maxRow;
mRow = 0;
mCol = 0;
%----------------------------------
% Mosaic title line
%----------------------------------
%if (nSPM > 0)
% axes('Position',[0 top 1 fVert]);
% axis off;
% Lstr = sprintf(' %s Ht thresh: %.2f Ext thresh: %.2f vox', SPM(1).title, SPM(1).u, SPM(1).k);
% text(1-8/WIN(3), -2*fVert, Lstr, 'Units', 'normalized',...
% 'FontSize', 10,...
% 'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');
%end;
% --------------------------------------------------------------------------
% orthoslice parameters
% Vsize is size (mm) of background bounding box.
% --------------------------------------------------------------------------
Vsize = diff(bbox)'+1;
%-------------------------------------------------------------------------
% Loop over images in Mosaic
%-------------------------------------------------------------------------
for n = 1:nImgs
% --------------------------------------------------------------------------
% get the slice at location, L, of the background image
%----------------------------------------------------------------------------
L = TalL; %VbgVox.*round(TalL./VbgVox);
switch orCode
case 1 % SAGITTAL
T0 = [0 1 0 -bbox(1,2)+1 ;...
0 0 1 -bbox(1,3)+1 ;...
1 0 0 -L(1) ;...
0 0 0 1];
i = 2; j = 3;
case 2 % CORONAL
T0 = [1 0 0 -bbox(1,1)+1 ;...
0 0 1 -bbox(1,3)+1 ;...
0 1 0 -L(2) ;...
0 0 0 1];
i = 1; j = 3;
case 3 % TRANSAXIAL
T0 = [1 0 0 -bbox(1,1)+1 ;...
0 1 0 -bbox(1,2)+1 ;...
0 0 1 -L(3) ;...
0 0 0 1 ];
i = 1; j = 2;
end
sliceDims = [Vsize(i) Vsize(j)];
D = spm_slice_vol(Vbg, inv(T0*A), sliceDims, 1);
% ----------------------------------------------------------
% Contrast enhancement of background (optional)
% ----------------------------------------------------------
if contrastEnhance
%---------------------------------------------------------
% Contrast enhance all but the highest intensities
% which prevents a small number of high-intensity pixels
% from skewing the scale factor.
%---------------------------------------------------------
Dmin = 0;
[histo, bin] = hist(D(:), 100);
cs = cumsum(histo);
i = min(find(cs > .97*max(cs)));
scale = i*max(D(:))/100;
% -----------------------------------------------------------------
% Unused alternate contrast enhancement algorithms.
% -----------------------------------------------------------------
%----------------------------------------------
% 1. Contrast enhance the high intensities
%----------------------------------------------
% Dmin = .1*max(D(:));
% scale = max(D(:)) - Dmin;
%---------------------------------------------------
% 2. Contrast enhance via histogram equalization
% (This is relatively slow)
%---------------------------------------------------
% [histo, bin] = hist(D(:), 100);
% cs = cumsum(histo);
% gLo = 0; gHi = 0;
% binSize = max(cs)/nGrays; % pixels per gray level
% lim = binSize; % initialize to binSize
% maxD = max(D(:));
% for n = 1:nGrays
% i = min(find(cs >= lim));
% gHi = round(i*maxD/100);
% disp([num2str(n) ': ' num2str(gLo) ' - ' num2str(gHi)]);
% i = find( (D >= gLo) & (D <= gHi));
% D(i) = (n-1)*ones(1,length(i));
% gLo = gHi;
% lim = lim + binSize;
% end;
% Dmin = 0;
% scale = max(D(:));
else
Dmin = 0;
scale = max(D(:));
end
if scale == 0; scale = 1; end; % scale for a completely zero-intensity image
%----------------------------------------------------------
% Scale the background image, D, from 0 to 1;
%----------------------------------------------------------
Dbg = (D - Dmin)/scale;
% --------------------------------------------------------
% Force outliers into interval [0, 1]
% --------------------------------------------------------
i = find(Dbg < 0);
Dbg(i) = zeros(1,length(i));
i = find(Dbg > 1);
Dbg(i) = ones(1,length(i));
%---------------------------------------------------------
% Scale the background image into gray color range
%---------------------------------------------------------
Dbg = nGrays*Dbg;
%---------------------------------------------------------
% Construct a composite of SPM overlays, iSPM
% each non-zero activation takes a value of 2^(n-1)
%---------------------------------------------------------
iSPM = zeros(round(sliceDims));
for k = 1:nSPM
% ----------------------------------------------------------------
% Logic extracted from 'addblobs' section of spm_orthviews.m 2.25
% ----------------------------------------------------------------
rcp = round(SPMVol(k).XYZ);
dim = max(rcp, [], 2)';
off = rcp(1,:) + dim(1)*(rcp(2,:)-1 + dim(2)*(rcp(3,:)-1));
Vspm = zeros(dim);
Vspm(off) = SPMVol(k).Z;
Vspm = reshape(Vspm, dim);
T = spm_slice_vol(Vspm, inv(T0*SPMExtras(k).M), sliceDims, SPMinterp);
%--------------------------------------------------------
% Merge Overlay, T, with composite overlay image, iSPM.
%--------------------------------------------------------
if nSPM == 1
Zmax = max(SPMVol(k).Z);
iSPM = T/Zmax; % Scale overlay values from [0, 1]
% ------------------------------------------------------------
% Force any outliers (via interpolation) into interval [0, 1]
% ------------------------------------------------------------
i = find(iSPM < 0);
iSPM(i) = zeros(1, length(i));
i = find(iSPM > 1);
iSPM(i) = ones(1, length(i));
% --------------------------------------------------------------------------------
% Interpolation may have produced voxels with small, non-zero values (< 1/segSize)
% Force voxels with values (.5-1)/segSize into the lowest level of the
% color segment for this SPM.
% Otherwise they will be mapped into the maximum value of the gray scale (white)
% (or the max value of the previous color segment)
% --------------------------------------------------------------------------------
i = find((iSPM > 0) & (iSPM < .5/segSize));
if isempty(i)
iSPM(i) = zeros(1, length(i));
end
i = find((iSPM > 0) & (iSPM < 1/segSize));
if isempty(i)
iSPM(i) = (1/nGrays)*ones(1, length(i));
end
i = find(iSPM);
iSPM(i) = round(segSize*iSPM(i)) + 1;
else
% ------------------------------------------------------------
% Multiple SPM overlays are composited by assigning a value
% of 2^(n-1) to each activation.
% ------------------------------------------------------------
i = find(T);
nn = 2^(k-1);
iSPM(i) = iSPM(i) + nn;
end;
end;
%---------------------------------------------------------------
% Merge Composite of SPM Overlays, iSPM, with background, Dbg.
%----------------------------------------------------------------
i = find(iSPM);
Dbg(i) = nGrays + iSPM(i);
if gcf ~= Fgraph; figure(Fgraph); end;
axes('Position',[mCol*(w+fHoriz) top-(mRow+1)*(h+fVert)-fVert w h])
Timg = rot90(Dbg);
if orCode == 1 % Sagittal
Timg = fliplr(Timg);
end;
himg = image(Timg,'tag','nbilmosaic');
axis image; axis off;
Lstr = sprintf('%s %0.3g mm', xLbl(orCode,:), L(orCode));
text(.5, -.01*h, Lstr, 'Units', 'normalized',...
'FontSize', 9,...
'HorizontalAlignment', 'center', 'VerticalAlignment', 'top');
TalL(orCode) = TalL(orCode) + spacing; % increment by user-specifed spacing.
L = TalL; % VbgVox.*round(TalL./VbgVox);
if L(orCode) > startMax; break; end; %(VbgDim(orCode)-VbgOrg(orCode))*VbgVox(orCode); break; end;
if L(orCode) < startMin; break; end; %(-VbgOrg(orCode))*VbgVox(orCode); break; end;
mCol = mCol+1;
if mCol == maxCol
mCol = 0;
mRow = mRow + 1;
drawnow;
end;
end; % endfor nImgs
%--------------------------------------
% add colorbar(s) beneath tiles
%--------------------------------------
x = [0.02 .27 .52 .77];
y = 3*fVert*[1 1 1 1];
for n = 1:nSPM
nn = 2^(n-1);
s1 = nGrays + (nn-1)*segSize + 1;
s2 = s1 + segSize - 1;
hAx = axes('Position', [x(n) y(n) .20 16/WIN(4)]);
image(s1:s2);
ZminStr = sprintf('%.2f', min(SPMVol(n).Z));
ZmaxStr = sprintf('%.2f', max(SPMVol(n).Z));
text(0.0, -fVert, ZminStr, 'Units','normalized', 'FontSize', 9,...
'HorizontalAlignment', 'left', 'VerticalAlignment', 'top');
text(0.0, -35*fVert, SPMVol(n).title, 'Units','normalized', 'FontSize', 9,...
'HorizontalAlignment', 'left', 'VerticalAlignment', 'top');
text(1.0, -fVert, ZmaxStr, 'Units','normalized', 'FontSize', 9,...
'HorizontalAlignment', 'right', 'VerticalAlignment', 'top');
axis off;
end;
%--------------------------------------------------
% add color overlap icons beneath colorbar(s)
%--------------------------------------------------
pairs = [ 1 3 2; 1 5 4; 2 6 4; 1 9 8; 2 10 8; 4 12 8] + nGrays;
triples = [0 1 0 2 7 4; 0 1 0 2 11 8; 0 1 0 4 13 8; 0 2 0 4 14 8] + nGrays;
for n = 1:nSPM
for nn = 1:n*(n-1)/2
hAx = axes('Position', [.02+.10*(nn-1) .7*fVert .08 12/WIN(4)]);
image(pairs(nn,:));
axis off;
end;
combos = [0 0 1 4]; % n!(n-m)!/m! for n=3,4 and m=3
nnmax = combos(n);
for nn = 1:nnmax
p = reshape(triples(nn,:), 3, 2)';
hAx = axes('Position', [.65+.08*(nn-1) .01 .06 fVert]);
image(p);
axis off;
end;
end;
%--------------------------------------
% Reset mouse pointer
%--------------------------------------
set([Finter,Fgraph],'Pointer','Arrow')
%----------------------------------------------------------------------------
% Install the mosaic ROI handler if requested
% and the ROI handler is available.
%----------------------------------------------------------------------------
if (nSPM > 0) && nbilmosaic.drawROI && exist('nbilmosaicROI')
nbilmosaicROI('install');
end;
return;
%=========================================================================
% DRAW ROI CHECKBOX CALLBACK
%=========================================================================
function drawROICB
% --------------------------------------------------
% Retrieve the nbilmosaic struct from UserData.
% --------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
nbilmosaic = get(h, 'UserData');
nbilmosaic.drawROI = get(gcbo, 'Value');
set(h, 'UserData', nbilmosaic);
if nbilmosaic.drawROI
str = 'on';
action = 'install';
else
str = 'off';
action = 'uninstall';
end;
nbil_mosaicROI(action);
set(nbilmosaic.hDrawROIPrompt, 'Visible', str);
return;
%=========================================================================
% STARTING COORDINATE CALLBACK
%=========================================================================
function stCoordCB
% ----------------------------------------------------------------
%
% Callback for startCoord edit uicontrol
%
% ----------------------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
% --------------------------------------------------
% Retrieve the nbilmosaic struct from UserData.
% --------------------------------------------------
nbilmosaic = get(h, 'UserData');
Vol = nbilmosaic.Vol;
orCode = nbilmosaic.orCode;
% -----------------------------------------------------------
% Retrieve current starting coordinate type-in value
% Validate & store result back in 'UserData' & edit box
% -----------------------------------------------------------
startMin = -Vol.org(orCode)*Vol.vox(orCode);
startMax = (Vol.dim(orCode)-Vol.org(orCode))*Vol.vox(orCode);
startCoord = round(str2num(get(gcbo, 'String')));
n = round((startCoord - startMin)/Vol.vox(orCode));
startCoord = startMin + n*Vol.vox(orCode);
if startCoord < startMin
startCoord = startMin;
end;
if startCoord > startMax
startCoord = startMax;
end;
set(gcbo, 'String', num2str(startCoord));
nbilmosaic.startCoord = startCoord;
set(h, 'UserData', nbilmosaic);
return;
%=========================================================================
% SLICE CALLBACK
%=========================================================================
function SliceCB(action)
% --------------------------------------------------
% Retrieve the nbilmosaic struct from UserData.
% --------------------------------------------------
Finter = spm_figure('FindWin','Interactive');
h = findobj(Finter, 'Tag', 'nbilmosaic');
if isempty(h); return; end;
nbilmosaic = get(h, 'UserData');
Vol = nbilmosaic.Vol;
orCode = nbilmosaic.orCode;
% -----------------------------------------------------------
% Retrieve current starting coordinate type-in value
% Validate & store result back in 'UserData' & edit box
% -----------------------------------------------------------
startMin = -Vol.org(orCode)*Vol.vox(orCode);
startMax = (Vol.dim(orCode)-Vol.org(orCode))*Vol.vox(orCode);
startCoord = round(str2num(get(findobj('Tag','nbilmosaicStCoord'), 'String')));
if isempty(startCoord)
startCoord = startMin;
end
n = round((startCoord - startMin)/Vol.vox(orCode));
startCoord = startMin + n*Vol.vox(orCode);
% assume that the last slice displayed is related to the number of rows and
% columns of the display and the mosaic tile direction.
lastslice = startCoord + (nbilmosaic.direction * ...
((nbilmosaic.maxRow * nbilmosaic.maxCol)-1) * nbilmosaic.spacing);
if strcmp(action,'low') && (nbilmosaic.direction == 1)
startCoord = startCoord - (nbilmosaic.maxRow * nbilmosaic.maxCol * ...
nbilmosaic.spacing);
elseif strcmp(action,'low') && (nbilmosaic.direction == -1)
startCoord = startCoord + (nbilmosaic.maxRow * nbilmosaic.maxCol * ...
nbilmosaic.spacing);
elseif strcmp(action,'high') && (nbilmosaic.direction == 1)
startCoord = lastslice +nbilmosaic.spacing;
elseif strcmp(action,'high') && (nbilmosaic.direction == -1)
startCoord = lastslice - nbilmosaic.spacing;
end
if startCoord < startMin
startCoord = startMin;
end;
if startCoord > startMax
startCoord = startMax;
end;
set(findobj('Tag','nbilmosaicStCoord'), 'String', num2str(startCoord));
nbilmosaic.startCoord = startCoord;
set(h, 'UserData', nbilmosaic);
drawMosaic
return;
%=========================================================================
% STR2FINTER
%=========================================================================
function h = str2finter(str, nLine)
%STR2FINTER(str, nLine) -- puts a string on line nLine of SPM
%interactive figure
%
% A utility function to put a non-input string in the SPM interactive fig.
% Can be used to feedback information to user during an interactive
% session. The handle to the uicontrol created is returned in h, so that
% the text characteristics can be modified.
%
% 01/04/01 1.0 Baseline -- Roger Ray
% _______________________________________________________________________
% $Id: nbil_mosaic.m,v 1.6 2008-09-19 09:39:36-05 drg Exp drg $
Finter = spm_figure('FindWin', 'Interactive');
[FRec, QRec, PRec, RRec] = spm_input('!InputRects', nLine, '', Finter);
PRec(3) = PRec(3) + RRec(3);
h = uicontrol(Finter, 'Style', 'Text', 'String', str, 'Position', PRec);
return;
%=========================================================================
% NBIL_DEFAULTCMAP
%=========================================================================
function cmap = nbil_defaultcmap
% DEFAULTCMAP returns a default colormap for CBMGmosaic
% Inputs: None
% Outputs: Default colormap (15 x 3)
% $Id: nbil_mosaic.m,v 1.6 2008-09-19 09:39:36-05 drg Exp drg $
cmap=[ 0.0 0.9 0.22 ;... % green(h=.4)
0.0 0.4 1.0 ;... % blue (h=.55)
0.15 0.9 0.8 ;... % cyan(h=.475) (green+blue)
1.0 0.0 0.6 ;... % magenta (h=.90)
0.85 0.85 0.6 ;... % (green+magenta)
0.75 0.6 0.9 ;... % (blue+magenta)
0.8 0.8 0.9 ;... % (magenta+blue+green)
1.0 0.6 0.3 ;... % orange (h=.10)
0.9 0.8 0.4 ;... % yellow (orange+green)
0.7 0.8 0.7 ;... % green (orange+blue)
0.9 0.8 0.8 ;... % (orange+blue+green)
0.8 0.6 0.6 ;... % (orange+magenta)
0.8 0.8 0.9 ;... % (green+orange+magenta)
0.9 0.8 0.9 ;... % (blue+orange+magenta)
1.0 0.9 0.9 ]; % (green+blue+orange+magenta)
% AN EXAMPLE COLORMAP THAT ONLY USES 8 COLORS IN THE ORDER
% RED, GREEN, YELLOW, BLUE, PURPLE, CYAN, WHITE
% TO USE, UNCOMMENT THE LINES AND SAVE IN A FILE CALLED 8colors.rgb. Then
% select this colormap when nbil_mosaic asks which map to use.
% 1.0000 0.0000 0.0000
% 0.0000 1.0000 0.0000
% 1.0000 1.0000 0.0000
% 0.0000 0.0000 1.0000
% 1.0000 0.0000 1.0000
% 0.0000 1.0000 1.0000
% 1.0000 1.0000 1.0000
% 0 0 0
% 0 0 0
% 0 0 0
% 0 0 0
% 0 0 0
% 0 0 0
% 0 0 0
% 0 0 0
}}}
{{{
function [t, sts] = nbilgetselect(varargin)
% NBILGETSELECT Calls the appropriate spm file selector function.
%
% [T STS] = NBILGETSELECT(VARARGIN) The function is implemented exactly
% as one would call either spm_get or spm_select. The advantage is that
% NBILGETSELECT can be substituted directly for calls to either spm_get
% or spm_select without changing the rest of the line. The change can be
% made using the Find and Replace... function of the Matlab editor.
% NBILGETSELECT will then call the correct file selection function for
% the currently running version of SPM, i.e., SPM2 -> spm_get;
% SPM5 -> spm_select. T is a character array of the selected files. STS
% is a status flag. It is always empty for SPM2 and either 1=OK, or
% 0=window quit for SPM5.
%
% User notes: Both spm_get and spm_select have 2 functional modes. The
% first is for file selection. The user provides the number of files,
% a filter, the dialog string, etc. and a file dialog is presented
% allowing the user to choose the appropriate files. The second mode is
% action based based, e.g., to create the file dialog, etc.
%
% NBILGETSELECT can tranlate between spm_get and spm_select only for the
% first mode, but not the second. This happens because the action
% strings for spm_get may have no equivalent in spm_select and vice
% versa.
%
% Action strings from SPM2 that will not work in SPM5.
% createfig, figkeypressfcn, initialise, statusline, cd, dir,
% strsort, strfliplr, filesummary, add, delete, reset, all, cmdline,
% gui2cmdline, edit, editdone, done, drivespulldownstr, files
%
% Action strings from SPM5 that will not work in SPM2
% addvfiles, clearvfiles, vfiles, list
%
% Example:
% If the original Matlab statement was
% P = spm_get(1,'SPM.mat','Select SPM.mat');
% Changing it to
% P = nbilgetselect(1,'SPM.mat','Select SPM.mat');
% will now work in either SPM2 or SPM5.
%
% When non-supported action strings are used an error occurs.
% drvstr = spm_get('DrivesPullDownStr');
% works properly, but
% drvstr = nbilgetselect('DrivesPullDownStr');
% will produce an error
% ??? Error using ==> nbilgetselect
% SPM2 action string used but you are using SPM5
%
% See also spm_get and spm_select for the specific input arguments.
% Author(s): Darren Gitelman
% $Id: nbilgetselect.m,v 1.6 2007-08-22 14:03:08-05 drg Exp $
try
spmVer = spm('ver');
catch
error('A version of SPM is not in the Matlab path.')
end
t = [];
sts = [];
% first look for a known Action string.
if ~isnumeric(varargin{1})
switch lower(varargin{1})
case {'createfig','figkeypressfcn','initialise','statusline',...
'cd','dir','strsort','strfliplr','filesummary','add',...
'delete','reset','all','cmdline','gui2cmdline','edit',...
'editdone','done','drivespulldownstr','files'}
if strcmpi(spm('ver'),'SPM2')
t = spm_get(varargin{:});
return
else
error(['SPM2 action string used but you are using ',...
spm('ver'),'.']);
end
case {'addvfiles','clearvfiles','vfiles','list'}
if strcmpi(spm('ver'),'SPM5')
[t sts] = spm_select(varargin{:});
return
else
error(['SPM5 action string used, but you are using ',...
spm('ver'),'.']);
end
case 'cpath'
if strcmpi(spm('ver'),'SPM2')
t = spm_get(varargin{:});
return
elseif strcmpi(spm('ver'),'SPM5')
t = spm_select(varargin{:});
return
end
end
end
% if the action string was known, we would have exited.
% if action string is unknown then assume we are reading files or
% directories.
switch spmVer
case 'SPM2'
if nargin >= 1
end
if nargin >= 2
switch varargin{2}
case 'image'
varargin{2} = 'IMAGE';
case {'any','batch'}
varargin{2} = '*';
case 'xml'
varargin{2} = '*.xml';
case 'mat'
varargin{2} = '*.mat';
case 'dir'
varargin{1} = -1;
varargin{2} = '*';
otherwise
% leave value alone
end
if (nargin == 4) && ~(exist(varargin{4}) ==7)
% check if 4th argument is a directory.
% if not then ignore it and warn user
warning('SPM2: using spm_get. Ignoring 4th argument.');
varargin(4) = [];
elseif (nargin >= 5)
% check if argument 5 is a 0 (gui) or 1 (cmdline)
if ~(varargin{5} ==0) && ~(varargin{5} ==1)
if ~(exist(varargin{5}) ==7)
warning('SPM2: using spm_get. Ignoring 5th argument');
varargin(5:end) = [];
else % must be set up as an spm5 call. move working
% directory to varargin{4}
varargin{4} = varargin{5};
% remove varargin(5) by setting cell to empty.
varargin(5:end) = [];
end
% if argument 5 was a 0 or one check if argument 4 is a
% directory.
elseif ~(exist(varargin{4}) ==7)
warning('SPM2: using spm_get. Ignoring arguments 4 and greater.');
varargin(4:end) = [];
end % check 5 arguments
end % nargin >=5
end % nargin >=2
t = spm_get(varargin{:});
case 'SPM5'
if nargin > 0
if any(varargin{1} == -1)
n = varargin{1};
n(1,1) = 1;
varargin{1} = n;
varargin{2} = 'dir';
end
if (nargin == 4) || (nargin == 5)
if iscell(varargin{4})
tmp = varargin{4}{1};
else
tmp = varargin{4};
end
if (exist(tmp) ==7)
% if 4th argument is a directory then this must
% have been written for spm2.
varargin{5} = tmp;
varargin{4} = {''};
end
end
end
[t, sts] = spm_select(varargin{:});
otherwise
warning('Unknown version of SPM.')
t = 0;
end
}}}
{{{
function varargout = nifti2ana(P)
% NIFTI2ANA converts images from nifti to analyze format readable in SPM2
% V = NIFTI2ANA(P) converts files that are in SPM5's nifti format to the
% analyze format that was readable by SPM2. This allows one to use files
% processed in SPM5 to be analyzed in SPM2.
%
% P are the filenames to be converted. If P is empty the user is
% prompted to select the files.
%
% V is a structure array of mapped volumes. This can be used to read the
% new files or discarded.
%
% SPM5 must be in the Matlab path.
%
% The converted files are prepended with 'n2a_'
%
% The program REQUIRES SPM5 image files (either .nii or .img) as input,
% and outputs SPM2 compatible analyze-type files. This program is not
% for converting SPM5 .nii to SPM5 .img files. To do that simply read
% in the data, change the filename to have a .img extension, and write
% out the new file. For example:
% v = spm_vol('my_file_name.nii');
% img = spm_read_vols(v);
% vnew = v;
% vnew.fname = [vnew.fname(1:end-3), 'img'];
% vnew = spm_create_vol(vnew);
% vnew = spm_write_vol(vnew,img);
%
% Author: Darren Gitelman
% $Id: nifti2ana.m,v 1.3 2008-07-22 13:06:43-05 drg Exp drg $
% updated 2010-07-14 by drg.
% Get image filenames if none are provided
if nargin < 1
P = spm_select(Inf,'image','Select images to convert');
end
if isempty(P)
disp('No files selected. Aborting conversion.')
return;
end
% Map the volumes using SPM5 file handling tools
fprintf('Reading SPM5 NIFTI volume headers..');
Vin = spm_vol(deblank(P));
fprintf('Done\n');
fprintf('There are %i files to be converted.\n',numel(Vin));
Vout = struct('fname' , '',...
'dim' , [],...
'mat' , [],...
'pinfo' , [],...
'descrip', '',...
'n' , [],...
'private', []);
V = Vout;
% Convert the volume structures to be analyze compatible
fprintf('Creating SPM2-compatible headers..')
for i = 1:numel(Vin)
fprintf('%5i',i);
[tmppth tmpfn tmpext] = fileparts(Vin(i).fname);
dt = Vin(i).dt(1);
tmpfn = ['n2a_',tmpfn];
Vtmp.fname = fullfile(tmppth,[tmpfn,tmpext]);
Vtmp.dim = [Vin(i).dim dt];
Vtmp.mat = Vin(i).mat;
if spm_flip_analyze_images
fprintf(' : Flipping image L/R\n')
Vtmp.mat = spm_matrix([0 0 0 0 0 0 -1 1 1 0 0 0])*Vtmp.mat;
fprintf('Creating SPM2-compatible headers..%5i',i)
end
Vtmp.pinfo = Vin(i).pinfo;
Vtmp.descrip = ['nifti2analyze converted: ',Vin(i).descrip];
dtype = Vin(i).private.dat.dtype;
% must pass the dtype to figure out if endianness is swapped.
Vout(i)= my_spm2_create_vol(Vtmp,'noopen',dtype);
fprintf(sprintf(repmat('\b',1,5)))
end
fprintf('Done\n')
fprintf('Writing SPM2-compatible image files..')
for i = 1:numel(Vout)
fprintf('%5i',i);
y = spm_read_vols(Vin(i));
V(i) = my_spm2_write_vol(Vout(i),y,dtype);
fprintf(sprintf(repmat('\b',1,5)))
end
fprintf('All Done!\n')
if nargout == 1
varargout{1} = V;
end
%_______________________________________________________________________
%_______________________________________________________________________
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% THESE FUNCTIONS COME FROM THE SPM2 VERSION OF SPM_CREATE_VOL
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function V = my_spm2_create_vol(V,varargin)
% Create an image file.
% FORMAT Vo = spm_create_vol(Vi,['noopen'])
% Vi - data structure containing image information.
% - see spm_vol for a description.
% 'noopen' - optional flag to say "don't open/create the image file".
% Vo - data structure after modification for writing.
% varargin can now have 2 arguments, 'noopen' and the dtype.
%_______________________________________________________________________
% @(#)spm_create_vol.m 2.14 John Ashburner 03/07/31
for i=1:numel(V)
if nargin>1,
v = my_spm2_internal_create_vol(V(i),varargin{:});
else
v = my_spm2_internal_create_vol(V(i));
end;
f = fieldnames(v);
for j=1:size(f,1),
%eval(['V(i).' f{j} ' = v.' f{j} ';']);
V = setfield(V,{i},f{j},getfield(v,f{j}));
end;
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function V = my_spm2_internal_create_vol(V,varargin)
if ~isfield(V,'n') || isempty(V.n)
V.n = 1;
end;
if ~isfield(V,'descrip') || isempty(V.descrip)
V.descrip = 'SPM2 compatible';
end;
V.private = struct('hdr',[]);
% Orientation etc...
M = V.mat;
if spm_flip_analyze_images, M = diag([-1 1 1 1])*M; end;
vx = sqrt(sum(M(1:3,1:3).^2));
if det(M(1:3,1:3))<0, vx(1) = -vx(1); end;
origin = M\[0 0 0 1]';
origin = round(origin(1:3));
[pth nam] = fileparts(V.fname);
fname = fullfile(pth,[nam, '.hdr']);
try
[hdr swapped] = my_spm2_spm_read_hdr(fname);
catch
warning(['Could not read "' fname '"']);
swapped = 0;
hdr = [];
end;
if ~isempty(hdr) && (hdr.dime.dim(5)>1 || V.n>1),
% cannot simply overwrite the header
hdr.dime.dim(5) = max(V.n,hdr.dime.dim(5));
if any(V.dim(1:3) ~= hdr.dime.dim(2:4))
error('Incompatible image dimensions');
end;
if sum((vx-hdr.dime.pixdim(2:4)).^2)>1e-6,
error('Incompatible voxel sizes');
end;
V.dim(4) = spm_type(spm_type(hdr.dime.datatype));
mach = 'native';
if swapped,
V.dim(4) = V.dim(4)*256;
if spm_platform('bigend'),
mach = 'ieee-le';
else
mach = 'ieee-be';
end;
end;
if isfinite(hdr.dime.funused1) && hdr.dime.funused1
scal = hdr.dime.funused1;
if isfinite(hdr.dime.funused2),
dcoff = hdr.dime.funused2;
else
dcoff = 0;
end;
else
if hdr.dime.glmax-hdr.dime.glmin && hdr.dime.cal_max-hdr.dime.cal_min
scal = (hdr.dime.cal_max-hdr.dime.cal_min)/(hdr.dime.glmax-hdr.dime.glmin);
dcoff = hdr.dime.cal_min - scal*hdr.dime.glmin;
else
scal = 1;
dcoff = 0;
warning(['Assuming a scalefactor of 1 for "' V.fname '".']);
end;
end;
V.pinfo(1:2) = [scal dcoff]';
V.private.hdr = hdr;
else
V.private.hdr = my_spm2_create_defaults;
my_endianess = spm_platform('bigend');
if nargin == 3
dtype = varargin{2};
elseif nargin ==2
dtype = varargin{1};
end
if my_endianess && ~isempty(findstr(dtype,'BE')) || ...
~my_endianess && isempty(findstr(dtype,'BE'))
swapped = 0;
else
swapped = 1;
end
dt = spm_type(spm_type(V.dim(4)));
if any(dt == [128+2 128+4 128+8]),
% Convert to a form that Analyze will support
dt = dt - 128;
end;
V.dim(4) = dt;
mach = 'native';
if swapped
V.dim(4) = V.dim(4)*256;
if spm_platform('bigend'),
mach = 'ieee-le';
else
mach = 'ieee-be';
end;
end;
V.private.hdr.dime.datatype = dt;
V.private.hdr.dime.bitpix = spm_type(dt,'bits');
if spm_type(dt,'intt'),
V.private.hdr.dime.glmax = spm_type(dt,'maxval');
V.private.hdr.dime.glmin = spm_type(dt,'minval');
if 0, % Allow DC offset
V.private.hdr.dime.cal_max = max(V.private.hdr.dime.glmax*V.pinfo(1,:) + V.pinfo(2,:));
V.private.hdr.dime.cal_min = min(V.private.hdr.dime.glmin*V.pinfo(1,:) + V.pinfo(2,:));
V.private.hdr.dime.funused1 = 0;
scal = (V.private.hdr.dime.cal_max - V.private.hdr.dime.cal_min)/...
(V.private.hdr.dime.glmax - V.private.hdr.dime.glmin);
dcoff = V.private.hdr.dime.cal_min - V.private.hdr.dime.glmin*scal;
V.pinfo = [scal dcoff 0]';
else % Don't allow DC offset
cal_max = max(V.private.hdr.dime.glmax*V.pinfo(1,:) + V.pinfo(2,:));
cal_min = min(V.private.hdr.dime.glmin*V.pinfo(1,:) + V.pinfo(2,:));
V.private.hdr.dime.funused1 = cal_max/V.private.hdr.dime.glmax;
if V.private.hdr.dime.glmin,
V.private.hdr.dime.funused1 = max(V.private.hdr.dime.funused1,...
cal_min/V.private.hdr.dime.glmin);
end;
V.private.hdr.dime.cal_max = V.private.hdr.dime.glmax*V.private.hdr.dime.funused1;
V.private.hdr.dime.cal_min = V.private.hdr.dime.glmin*V.private.hdr.dime.funused1;
V.pinfo = [V.private.hdr.dime.funused1 0 0]';
end;
else
V.private.hdr.dime.glmax = 1;
V.private.hdr.dime.glmin = 0;
V.private.hdr.dime.cal_max = 1;
V.private.hdr.dime.cal_min = 0;
V.private.hdr.dime.funused1 = 1;
end;
V.private.hdr.dime.pixdim(2:4) = vx;
V.private.hdr.dime.dim(2:4) = V.dim(1:3);
V.private.hdr.dime.dim(5) = V.n;
V.private.hdr.hist.origin(1:3) = origin;
d = 1:min([length(V.descrip) 79]);
V.private.hdr.hist.descrip = char(zeros(1,80));
V.private.hdr.hist.descrip(d) = V.descrip(d);
V.private.hdr.hk.db_name = char(zeros(1,18));
[pth, nam] = fileparts(V.fname);
d = 1:min([length(nam) 17]);
V.private.hdr.hk.db_name(d) = nam(d);
end;
V.pinfo(3) = prod(V.private.hdr.dime.dim(2:4))*V.private.hdr.dime.bitpix/8*(V.n-1);
fid = fopen(fname,'w',mach);
if (fid == -1),
error(['Error opening ' fname '. Check that you have write permission.']);
end;
my_spm2_write_hk(fid,V.private.hdr.hk);
my_spm2_write_dime(fid,V.private.hdr.dime);
my_spm2_write_hist(fid,V.private.hdr.hist);
fclose(fid);
fname = fullfile(pth,[nam, '.mat']);
off = -vx'.*origin;
mt = [vx(1) 0 0 off(1) ; 0 vx(2) 0 off(2) ; 0 0 vx(3) off(3) ; 0 0 0 1];
if spm_flip_analyze_images, mt = diag([-1 1 1 1])*mt; end;
if sum((V.mat(:) - mt(:)).*(V.mat(:) - mt(:))) > eps*eps*12 || exist(fname,'file')==2
if exist(fname,'file')==2,
clear mat
str = load(fname);
if isfield(str,'mat'),
mat = str.mat;
elseif isfield(str,'M'),
mat = str.M;
if spm_flip_analyze_images,
for i=1:size(mat,3),
mat(:,:,i) = diag([-1 1 1 1])*mat(:,:,i);
end;
end;
end;
mat(:,:,V.n) = V.mat;
mat = my_spm2_fill_empty(mat,mt);
M = mat(:,:,1);
if spm_flip_analyze_images
M = diag([-1 1 1 1])*M;
end;
try
save(fname,'mat','M','-append');
catch % Mat-file was probably Matlab 4
save(fname,'mat','M');
end;
else
clear mat
mat(:,:,V.n) = V.mat;
mat = my_spm2_fill_empty(mat,mt);
M = mat(:,:,1);
if spm_flip_analyze_images
M = diag([-1 1 1 1])*M;
end;
save(fname,'mat','M');
end;
end;
if nargin==1 || ~strcmp(varargin{1},'noopen')
fname = fullfile(pth,[nam, '.img']);
V.private.fid = fopen(fname,'r+',mach);
if (V.private.fid == -1),
V.private.fid = fopen(fname,'w',mach);
if (V.private.fid == -1),
error(['Error opening ' fname '. Check that you have write permission.']);
end;
end;
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function Mo = my_spm2_fill_empty(Mo,Mfill)
todo = [];
for i=1:size(Mo,3)
if ~any(any(Mo(:,:,i))),
todo = [todo i];
end;
end;
if ~isempty(todo)
for i=1:length(todo),
Mo(:,:,todo(i)) = Mfill;
end;
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function my_spm2_write_hk(fid,hk)
% write (struct) header_key
%-----------------------------------------------------------------------
fseek(fid,0,'bof');
fwrite(fid,hk.sizeof_hdr, 'int32');
fwrite(fid,hk.data_type, 'char' );
fwrite(fid,hk.db_name, 'char' );
fwrite(fid,hk.extents, 'int32');
fwrite(fid,hk.session_error,'int16');
fwrite(fid,hk.regular, 'char' );
if fwrite(fid,hk.hkey_un0, 'char' )~= 1,
error(['Error writing ' fopen(fid) '. Check your disk space.']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function my_spm2_write_dime(fid,dime)
% write (struct) image_dimension
%-----------------------------------------------------------------------
fseek(fid,40,'bof');
fwrite(fid,dime.dim, 'int16');
fwrite(fid,dime.vox_units, 'uchar' );
fwrite(fid,dime.cal_units, 'uchar' );
fwrite(fid,dime.unused1, 'int16' );
fwrite(fid,dime.datatype, 'int16');
fwrite(fid,dime.bitpix, 'int16');
fwrite(fid,dime.dim_un0, 'int16');
fwrite(fid,dime.pixdim, 'float');
fwrite(fid,dime.vox_offset, 'float');
fwrite(fid,dime.funused1, 'float');
fwrite(fid,dime.funused2, 'float');
fwrite(fid,dime.funused2, 'float');
fwrite(fid,dime.cal_max, 'float');
fwrite(fid,dime.cal_min, 'float');
fwrite(fid,dime.compressed, 'int32');
fwrite(fid,dime.verified, 'int32');
fwrite(fid,dime.glmax, 'int32');
if fwrite(fid,dime.glmin, 'int32')~=1,
error(['Error writing ' fopen(fid) '. Check your disk space.']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function my_spm2_write_hist(fid,hist)
% write (struct) data_history
%-----------------------------------------------------------------------
fseek(fid,148,'bof');
fwrite(fid,hist.descrip, 'uchar');
fwrite(fid,hist.aux_file, 'uchar');
fwrite(fid,hist.orient, 'uchar');
fwrite(fid,hist.origin, 'int16');
fwrite(fid,hist.generated, 'uchar');
fwrite(fid,hist.scannum, 'uchar');
fwrite(fid,hist.patient_id, 'uchar');
fwrite(fid,hist.exp_date, 'uchar');
fwrite(fid,hist.exp_time, 'uchar');
fwrite(fid,hist.hist_un0, 'uchar');
fwrite(fid,hist.views, 'int32');
fwrite(fid,hist.vols_added, 'int32');
fwrite(fid,hist.start_field,'int32');
fwrite(fid,hist.field_skip, 'int32');
fwrite(fid,hist.omax, 'int32');
fwrite(fid,hist.omin, 'int32');
fwrite(fid,hist.smax, 'int32');
if fwrite(fid,hist.smin, 'int32')~=1,
error(['Error writing ' fopen(fid) '. Check your disk space.']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function hdr = my_spm2_create_defaults
hk.sizeof_hdr = 348;
hk.data_type = ['dsr ' 0];
hk.db_name = char(zeros(1,18));
hk.extents = 0;
hk.session_error= 0;
hk.regular = 'r';
hk.hkey_un0 = 0;
dime.dim = [4 0 0 0 1 0 0 0];
dime.vox_units = ['mm ' 0];
dime.cal_units = char(zeros(1,8));
dime.unused1 = 0;
dime.datatype = -1;
dime.bitpix = 0;
dime.dim_un0 = 0;
dime.pixdim = [0 1 1 1 1 0 0 0];
dime.vox_offset = 0;
dime.funused1 = 1;
dime.funused2 = 0;
dime.funused3 = 0;
dime.cal_max = 1;
dime.cal_min = 0;
dime.compressed = 0;
dime.verified = 0;
dime.glmax = 1;
dime.glmin = 0;
hist.descrip = char(zeros(1,80));
hist.descrip(1:length('SPM2 compatible')) = 'SPM2 compatible';
hist.aux_file = char(zeros(1,24));
hist.orient = char(0);
hist.origin = [0 0 0 0 0];
hist.generated = char(zeros(1,10));
hist.scannum = char(zeros(1,10));
hist.patient_id = char(zeros(1,10));
hist.exp_date = char(zeros(1,10));
hist.exp_time = char(zeros(1,10));
hist.hist_un0 = char(zeros(1,3));
hist.generated(1:5) = 'today';
hist.views = 0;
hist.vols_added = 0;
hist.start_field= 0;
hist.field_skip = 0;
hist.omax = 0;
hist.omin = 0;
hist.smax = 0;
hist.smin = 0;
hdr.hk = hk;
hdr.dime = dime;
hdr.hist = hist;
return;
%_______________________________________________________________________
%_______________________________________________________________________
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% THESE FUNCTIONS COME FROM THE SPM2 VERSION OF SPM_READ_HDR
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [hdr,otherendian] = my_spm2_spm_read_hdr(fname)
% Read (SPM customised) Analyze header
% FORMAT [hdr,otherendian] = spm_read_hdr(fname)
% fname - .hdr filename
% hdr - structure containing Analyze header
% otherendian - byte swapping necessary flag
%_______________________________________________________________________
% @(#)spm_read_hdr.m 2.2 John Ashburner 03/07/17
fid = fopen(fname,'r','native');
otherendian = 0;
if (fid > 0)
dime = my_spm2_read_dime(fid);
if dime.dim(1)<0 || dime.dim(1)>15 % Appears to be other-endian
% Re-open other-endian
fclose(fid);
if spm_platform('bigend'), fid = fopen(fname,'r','ieee-le');
else fid = fopen(fname,'r','ieee-be'); end;
otherendian = 1;
dime = my_spm2_read_dime(fid);
end;
hk = my_spm2_read_hk(fid);
hist = my_spm2_read_hist(fid);
hdr.hk = hk;
hdr.dime = dime;
hdr.hist = hist;
fclose(fid);
else
hdr = [];
otherendian = NaN;
%error(['Problem opening header file (' fopen(fid) ').']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function hk = my_spm2_read_hk(fid)
% read (struct) header_key
%-----------------------------------------------------------------------
fseek(fid,0,'bof');
hk.sizeof_hdr = fread(fid,1,'int32');
hk.data_type = my_spm2_mysetstr(fread(fid,10,'uchar'))';
hk.db_name = my_spm2_mysetstr(fread(fid,18,'uchar'))';
hk.extents = fread(fid,1,'int32');
hk.session_error = fread(fid,1,'int16');
hk.regular = my_spm2_mysetstr(fread(fid,1,'uchar'))';
hk.hkey_un0 = my_spm2_mysetstr(fread(fid,1,'uchar'))';
if isempty(hk.hkey_un0)
error(['Problem reading "hk" of header file (' fopen(fid) ').']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function dime = my_spm2_read_dime(fid)
% read (struct) image_dimension
%-----------------------------------------------------------------------
fseek(fid,40,'bof');
dime.dim = fread(fid,8,'int16')';
dime.vox_units = my_spm2_mysetstr(fread(fid,4,'uchar'))';
dime.cal_units = my_spm2_mysetstr(fread(fid,8,'uchar'))';
dime.unused1 = fread(fid,1,'int16');
dime.datatype = fread(fid,1,'int16');
dime.bitpix = fread(fid,1,'int16');
dime.dim_un0 = fread(fid,1,'int16');
dime.pixdim = fread(fid,8,'float')';
dime.vox_offset = fread(fid,1,'float');
dime.funused1 = fread(fid,1,'float');
dime.funused2 = fread(fid,1,'float');
dime.funused3 = fread(fid,1,'float');
dime.cal_max = fread(fid,1,'float');
dime.cal_min = fread(fid,1,'float');
dime.compressed = fread(fid,1,'int32');
dime.verified = fread(fid,1,'int32');
dime.glmax = fread(fid,1,'int32');
dime.glmin = fread(fid,1,'int32');
if isempty(dime.glmin)
error(['Problem reading "dime" of header file (' fopen(fid) ').']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function hist = my_spm2_read_hist(fid)
% read (struct) data_history
%-----------------------------------------------------------------------
fseek(fid,148,'bof');
hist.descrip = my_spm2_mysetstr(fread(fid,80,'uchar'))';
hist.aux_file = my_spm2_mysetstr(fread(fid,24,'uchar'))';
hist.orient = fread(fid,1,'uchar');
hist.origin = fread(fid,5,'int16')';
hist.generated = my_spm2_mysetstr(fread(fid,10,'uchar'))';
hist.scannum = my_spm2_mysetstr(fread(fid,10,'uchar'))';
hist.patient_id = my_spm2_mysetstr(fread(fid,10,'uchar'))';
hist.exp_date = my_spm2_mysetstr(fread(fid,10,'uchar'))';
hist.exp_time = my_spm2_mysetstr(fread(fid,10,'uchar'))';
hist.hist_un0 = my_spm2_mysetstr(fread(fid,3,'uchar'))';
hist.views = fread(fid,1,'int32');
hist.vols_added = fread(fid,1,'int32');
hist.start_field= fread(fid,1,'int32');
hist.field_skip = fread(fid,1,'int32');
hist.omax = fread(fid,1,'int32');
hist.omin = fread(fid,1,'int32');
hist.smax = fread(fid,1,'int32');
hist.smin = fread(fid,1,'int32');
if isempty(hist.smin)
error(['Problem reading "hist" of header file (' fopen(fid) ').']);
end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function out = my_spm2_mysetstr(in)
tmp = find(in == 0);
tmp = min([min(tmp) length(in)]);
out = char([in(1:tmp)' zeros(1,length(in)-(tmp))])';
return;
%_______________________________________________________________________
%_______________________________________________________________________
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% THESE FUNCTIONS COME FROM THE SPM2 VERSION OF SPM_WRITE_VOL
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function V = my_spm2_write_vol(V,Y,dtype)
% Write an image volume to disk, setting scales and offsets as appropriate
% FORMAT V = my_spm2_write_vol(V,Y)
% V (input) - a structure containing image volume information (see spm_vol)
% Y - a one, two or three dimensional matrix containing the image voxels
% V (output) - data structure after modification for writing.
%_______________________________________________________________________
% @(#)spm_write_vol.m 2.9 John Ashburner 03/02/26
if ndims(Y)>3, error('Can only handle a maximum of 3 dimensions.'), end
if ~isfield(V,'pinfo'), V.pinfo = [1,0,0]'; end
dim = [size(Y) 1 1 1];
if ~all(dim(1:3) == V.dim(1:3)) || (size(V.pinfo,2)~=1 && size(V.pinfo,2)~=dim(3)),
error('Incompatible dimensions.');
end
% Set scalefactors and offsets
%-----------------------------------------------------------------------
dt = V.dim(4); if dt>256, dt = dt/256; end;
if any(dt == [128+2 128+4 128+8]),
% Convert to a form that Analyze will support
dt = dt - 128;
end;
s = find(dt == [2 4 8 128+2 128+4 128+8]);
dmnmx = [0 -2^15 -2^31 -2^7 0 0 ; 2^8-1 2^15-1 2^31-1 2^7-1 2^16 2^32];
dmnmx = dmnmx(:,s);
V.pinfo(1,:) = 1;
V.pinfo(2,:) = 0;
mxs = zeros(dim(3),1)+NaN;
mns = zeros(dim(3),1)+NaN;
if ~isempty(s),
for p=1:dim(3),
tmp = double(Y(:,:,p));
tmp = tmp(isfinite(tmp));
if ~isempty(tmp),
mxs(p) = max(tmp);
mns(p) = min(tmp);
end;
end;
if size(V.pinfo,2) ~= 1
for p=1:dim(3),
mx = mxs(p);
mn = mns(p);
if ~isfinite(mx), mx = 0; end;
if ~isfinite(mn), mn = 0; end;
if mx~=mn,
V.pinfo(1,p) = (mx-mn)/(dmnmx(2)-dmnmx(1));
V.pinfo(2,p) = ...
(dmnmx(2)*mn-dmnmx(1)*mx)/(dmnmx(2)-dmnmx(1));
else
V.pinfo(1,p) = 0;
V.pinfo(2,p) = mx;
end;
end;
else
mx = max(mxs(isfinite(mxs)));
mn = min(mns(isfinite(mns)));
if isempty(mx), mx = 0; end;
if isempty(mn), mn = 0; end;
if mx~=mn
V.pinfo(1,1) = (mx-mn)/(dmnmx(2)-dmnmx(1));
V.pinfo(2,1) = (dmnmx(2)*mn-dmnmx(1)*mx)/(dmnmx(2)-dmnmx(1));
else
V.pinfo(1,1) = 0;
V.pinfo(2,1) = mx;
end;
end;
end;
%-Create and write image
%-----------------------------------------------------------------------
V = my_spm2_create_vol(V,dtype);
for p=1:V.dim(3),
V = my_spm2_write_plane(V,Y(:,:,p),p);
end;
V = my_spm2_close_vol(V);
%_______________________________________________________________________
%_______________________________________________________________________
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% THESE FUNCTIONS COME FROM THE SPM2 VERSION OF SPM_WRITE_PLANE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function V = my_spm2_write_plane(V,A,p)
% Write a transverse plane of image data.
% FORMAT V = my_spm2_write_plane(V,A,p)
% V - data structure containing image information.
% - see spm_vol for a description.
% A - the two dimensional image to write.
% p - the plane number (beginning from 1).
%
% VO - (possibly) modified data structure containing image information.
% It is possible that future versions of spm_write_plane may
% modify scalefactors (for example).
%
%_______________________________________________________________________
% @(#)spm_write_plane.m 2.19 John Ashburner 03/07/16
if any(V.dim(1:2) ~= size(A)), error('Incompatible image dimensions'); end;
if p>V.dim(3), error('Plane number too high'); end;
% Write Analyze image by default
V = my_spm2_write_analyze_plane(V,A,p);
return;
%_______________________________________________________________________
%_______________________________________________________________________
function V = my_spm2_write_analyze_plane(V,A,p)
types = [ 2 4 8 16 64 130 132 136, 512 1024 2048 4096 16384 33280 33792 34816];
maxval = [2^8-1 2^15-1 2^31-1 Inf Inf 2^7-1 2^16-1 2^32-1, 2^8-1 2^15-1 2^31-1 Inf Inf 2^8-1 2^16-1 2^32-1];
minval = [ 0 -2^15 -2^31 -Inf -Inf -2^7 0 0, 0 -2^15 -2^31 -Inf -Inf -2^7 0 0];
intt = [ 1 1 1 0 0 1 1 1, 1 1 1 0 0 1 1 1];
prec = str2mat('uint8','int16','int32','float','double','int8','uint16','uint32','uint8','int16','int32','float','double','int8','uint16','uint32');
swapped = [ 0 0 0 0 0 0 0 0, 1 1 1 1 1 1 1 1];
bits = [ 8 16 32 32 64 8 16 32, 8 16 32 32 64 8 16 32];
dt = find(types==V.dim(4));
if isempty(dt), error('Unknown datatype'); end;
A = double(A);
% Rescale to fit appropriate range
if intt(dt),
A(isnan(A)) = 0;
mxv = maxval(dt);
mnv = minval(dt);
A = round(A*(1/V.pinfo(1)) - V.pinfo(2));
A(A > mxv) = mxv;
A(A < mnv) = mnv;
end;
[pth,nam] = fileparts(V.fname);
fname = fullfile(pth,[nam, '.img']);
if ~isfield(V,'private') || ~isfield(V.private,'fid') || isempty(V.private.fid)
mach = 'native';
if swapped(dt)
if spm_platform('bigend')
mach = 'ieee-le';
else
mach = 'ieee-be';
end;
end;
fid = fopen(fname,'r+',mach);
if fid == -1
fid = fopen(fname,'w',mach);
if fid == -1
error(['Error opening ' fname '. Check that you have write permission.']);
end;
end;
else
if isempty(fopen(V.private.fid)),
mach = 'native';
if swapped(dt)
if spm_platform('bigend')
mach = 'ieee-le';
else
mach = 'ieee-be';
end;
end;
V.private.fid = fopen(fname,'r+',mach);
if V.private.fid == -1
error(['Error opening ' fname '. Check that you have write permission.']);
end;
end;
fid = V.private.fid;
end;
% Seek to the appropriate offset
datasize = bits(dt)/8;
off = (p-1)*datasize*prod(V.dim(1:2)) + V.pinfo(3,1);
fseek(fid,0,'bof'); % A bug in Matlab 6.5 means that a rewind is needed
if fseek(fid,off,'bof')==-1,
% Need this because fseek in Matlab does not seek past the EOF
fseek(fid,0,'bof'); % A bug in Matlab 6.5 means that a rewind is needed
fseek(fid,0,'eof');
curr_off = ftell(fid);
blanks = zeros(off-curr_off,1);
if fwrite(fid,blanks,'uchar') ~= numel(blanks)
my_spm2_write_error_message(V.fname);
error(['Error writing ' V.fname '.']);
end;
fseek(fid,0,'bof'); % A bug in Matlab 6.5 means that a rewind is needed
if fseek(fid,off,'bof') == -1,
my_spm2_write_error_message(V.fname);
error(['Error writing ' V.fname '.']);
end;
end;
if fwrite(fid,A,deblank(prec(dt,:))) ~= numel(A)
my_spm2_write_error_message(V.fname);
error(['Error writing ' V.fname '.']);
end;
if ~isfield(V,'private') || ~isfield(V.private,'fid') || isempty(V.private.fid), fclose(fid); end;
return;
%_______________________________________________________________________
%_______________________________________________________________________
function my_spm2_write_error_message(q)
str = {...
'Error writing:',...
' ',...
[' ',spm_str_manip(q,'k40d')],...
' ',...
'Check disk space / disk quota.'};
spm('alert*',str,mfilename,sqrt(-1));
return;
%_______________________________________________________________________
%_______________________________________________________________________
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% THIS FUNCTION COMES FROM THE SPM2 VERSION OF SPM_CLOSE_VOL
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function V = my_spm2_close_vol(V)
% Close image volume
% See: spm_create_vol and spm_write_plane.
%_______________________________________________________________________
% @(#)spm_close_vol.m 2.4 John Ashburner 02/08/16
for i=1:numel(V),
if isfield(V,'private') && isfield(V(i).private,'fid') && ~isempty(V(i).private.fid),
if ~isempty(fopen(V(i).private.fid)),
fclose(V(i).private.fid);
end;
V(i).private = rmfield(V(i).private,'fid');
end;
end;
}}}
{{{
function [sLorder midSlice] = nusliceorder(N,acq);
% NUSLICEORDER Returns slice numbers for slice timing calculations
% [SLORDER MIDSLICE] = NUSLICEORDER(N,ACQ) returns of vector of slice
% numbers SLORDER, and the middle slice number MIDSLICE for input into
% the SPM slice timing function. N is the number of slices and ACQ
% is the acquisition type: 'interleave', 'ascending', or 'descending'
%
% For interleaved sequences, the order can be calculated by generating
% a vector starting at the N-1 slice, counting back by two until the
% minimum non-negative slice is reached, then starting back up at N and
% counting back by 2 again. This vector is then L->R flipped.
%
% This update was based on our Siemens Trio slice ordering as
% reported by T. Parrish on May 25, 2006. This slice ordering may not
% be correct if the subject direction H->F vs. F->H or for a
% variety of other unfathomable reasons.
%
% NOTE: The contents of this function may change without notice or be
% entirely inaccurate even if you are using a Siemens Trio scanner.
% Unfortunately the files put out by the Siemens Trio software only tell
% the user if the acquisition type is interleaved, ascending or
% descending, but do not contain any other information about the actual
% acquisition order, which apparently can vary as the stars in the sky.
% Author: Darren Gitelman
% $Id: nusliceorder.m,v 1.2 2006-11-02 08:18:59-06 drg Exp drg $
if nargin < 2
error('Must include the number of slices and the acquisiton type.')
end
sLorder = [];
midSlice = [];
switch lower(acq)
case 'interleave'
sLorder = fliplr([N-1:-2:1, N:-2:1]);
case 'ascending'
sLorder = [1:N];
case 'descending'
sLorder = [N:-1:1];
otherwise
error(['You input an unknown sequence type of:', sprintf('%s',acq)]);
end
if rem(N,2)
midSlice = sLorder(round(N/2));
else
midSlice = sLorder(N/2+1);
end
}}}
{{{
function cleaneddata = removemovement(trialvec,movevec,TR,movethresh,pretime)
% REMOVEMOVEMENT Excludes trials with movement exceeding a threshold
% CLEANEDDATA = REMOVEMOVEMENT(TRIALVEC,MOVEVEC,TR,MOVETHRESH,PRETIME)
% Calculates translation movement based on SPM realignment parameters
% and excludes those onsets where the movement exceeds a specified
% threshold for a specified number of seconds preceeding the movement.
%
% TRIALVEC : Vector of onsets in SCAN time.
% MOVEVEC : Matrix of translation parameters (X, Y, Z) in mm.
% Rotations are ignored.
% TR : Scan repetition time. (secs)
% MOVETHRESH : Maximum allowed movement before excluding a trial.
% sqrt( (x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2 )
% PRETIME : Window in seconds for excluding trials prior to
% movements. A PRETIME of 16 means, exclude trials with an
% onset <= 16 seconds prior to movements exceeding
% MOVETHRESH.
%
% The output is a structure array with fields
% CLEANONSETVECTOR : Onset vector with trials exceeding MOVETHRESH
% excluded.
% INCLUDEDTRIALNUM : Number of each included trial (numbered
% consecutively from the suppled onset vector).
% EXCLUDEDTRIALNUM : Number of each excluded trial.
% MOVEMENTPERSCAN : Vector of total movements at each scan
% sqrt( (x2-x1)^2 + (y2-y1)^2 + (z2-z1)^2 )
%
% The rest of the fields are copied from the inputs, for bookeeping
% purposes, and include TRIALVEC, MOVEVEC, TR, MOVETHRESH, and PRETIME.
% Author: Darren Gitelman
% $Id: removemovement.m,v 1.1 2006-11-01 18:58:46-06 drg Exp drg $
if nargin < 5
error('Too few arguments. Enter help removemovement for a list of arguments.')
end
% set up output structure
cleaneddata = struct(...
'cleanOnsetVector', [],...
'includedTrialNum', [],...
'excludedTrialNum', [],...
'movementPerScan', [],...
'trialVec', [],...
'moveVec', movevec(:,1:3),...
'TR', TR,...
'moveThresh', movethresh,...
'preTime', pretime);
% Check if any trialvec > length(movevec)
if any(trialvec > length(movevec))
error('Some of the onsets exceed the maximum length of the run. Make sure onsets are in scans.')
end
% calculate movement
% movement = sqrt(movevec(:,1).^2 + movevec(:,2).^2 + movevec(:,3).^2);
totMove = sqrt(diff(movevec(:,1)).^2 + diff(movevec(:,2)).^2 + diff(movevec(:,3)).^2);
cleaneddata.movementPerScan = [0; totMove];
% make sure trialvec is sorted
[sTrialvec q] = sort(trialvec);
cleaneddata.trialVec = sTrialvec;
moveidx = [];
moveidx = find(totMove > movethresh);
if isempty(moveidx)
cleaneddata.cleanOnsetVector = sTrialvec;
cleaneddata.includedTrialNum = 1:length(sTrialvec);
return
end
% convert trialvec to time
sTrialvecSec = sTrialvec*TR;
for i = 1:length(moveidx)
movetime = (moveidx(i)*TR)-TR;
badtrialidx = find( (sTrialvecSec < movetime) & (sTrialvecSec > movetime-pretime) );
sTrialvecSec(badtrialidx) = NaN;
end
% remove NaN's and collapse trials
nanidx = isnan(sTrialvecSec);
notnanidx = ~nanidx;
trialIdx = 1:length(sTrialvec);
cleaneddata.cleanOnsetVector = sTrialvecSec(notnanidx)/TR;
cleaneddata.includedTrialNum = trialIdx(notnanidx);
cleaneddata.excludedTrialNum = trialIdx(nanidx);
return
}}}
NOTE: This function is Ged Ridgway's version of John Ashburner's reslice code. I think it works better than mine. -drg
Ged's web page http://www.cs.ucl.ac.uk/staff/G.Ridgway/vbm/ has a number of nice bits of Matlab code for SPM, and is where the lastest version of this function would be posted. This version is dated: 2007-03-05.
{{{
function resize_img(imnames, Voxdim, BB, ismask)
% RESIZE_IMG resample images to have specified voxel dims and BBox
% RESIZE_IMG(IMNAMES,VOXDIM,BB,ISMASK)
% Output images will be prefixed with 'r', and will have voxel
% dimensions equal to voxdim. Use NaNs to determine voxdims from
% transformation matrix of input image(s).
%
% If bb == nan(2,3), bounding box will include entire original image.
% Origin will move appropriately. Use world_bb to compute bounding box
% from a different image.
%
% If ismask == true then binary mask values are re-rounded to avoid
% growing or shrinking masks due to linear interp)
%
% See also voxdim, world_bb
% Based on John Ashburner's reorient.m
% http://www.sph.umich.edu/~nichols/JohnsGems.html#Gem7
% http://www.sph.umich.edu/~nichols/JohnsGems5.html#Gem2
% Adapted by Ged Ridgway -- email bugs to drc.spm@gmail.com
% Check spm version:
if exist('spm_select','file') % should be true for spm5
spm5 = 1;
elseif exist('spm_get','file') % should be true for spm2
spm5 = 0;
else
error('Can''t find spm_get or spm_select; please add SPM to path')
end
% set up defaults (including analyze.flip)
spm_defaults;
% prompt for missing arguments
if ( ~exist('imnames','var') || isempty(char(imnames)) )
if spm5
imnames = spm_select(inf, 'image', 'Choose images to resize');
else
imnames = spm_get(inf, 'img', 'Choose images to resize');
end
end
% check if inter fig already open, don't close later if so...
Fint = spm_figure('FindWin', 'Interactive'); Fnew = [];
if ( ~exist('Voxdim', 'var') || isempty(Voxdim) )
Fnew = spm_figure('GetWin', 'Interactive');
Voxdim = spm_input('Vox Dims (NaN for "as input")? ',...
'+1', 'e', '[nan nan nan]', 3);
end
if ( ~exist('BB', 'var') || isempty(BB) )
Fnew = spm_figure('GetWin', 'Interactive');
BB = spm_input('Bound Box (NaN => original)? ',...
'+1', 'e', '[nan nan nan; nan nan nan]', [2 3]);
end
if ~exist('ismask', 'var')
ismask = false;
end
if isempty(ismask)
ismask = false;
end
% reslice images one-by-one
vols = spm_vol(imnames);
for V=vols'
% (copy to allow defaulting of NaNs differently for each volume)
voxdim = Voxdim;
bb = BB;
% default voxdim to current volume's voxdim, (from mat parameters)
if any(isnan(voxdim))
vprm = spm_imatrix(V.mat);
vvoxdim = vprm(7:9);
voxdim(isnan(voxdim)) = vvoxdim(isnan(voxdim));
end
voxdim = abs(voxdim(:))'; % (handle analyze_flip separately)
mn = bb(1,:);
mx = bb(2,:);
% default BB to current volume's
if any(isnan(bb(:)))
vbb = world_bb(V);
vmn = vbb(1,:);
vmx = vbb(2,:);
mn(isnan(mn)) = vmn(isnan(mn));
mx(isnan(mx)) = vmx(isnan(mx));
end
% voxel [1 1 1] of output should map to BB mn
% (the combination of matrices below first maps [1 1 1] to [0 0 0])
mat = spm_matrix([mn 0 0 0 voxdim])*spm_matrix([-1 -1 -1]);
% voxel-coords of BB mx gives number of voxels required
% (round up if more than a tenth of a voxel over)
imgdim = ceil(mat \ [mx 1]' - 0.1)';
% reflect in x if required
if spm_flip_analyze_images; mat = diag([-1 1 1 1])*mat; end;
% output image
VO = V;
[pth,nam,ext] = fileparts(V.fname);
VO.fname = fullfile(pth,['r' nam ext]);
VO.dim(1:3) = imgdim(1:3);
VO.mat = mat;
VO = spm_create_vol(VO);
spm_progress_bar('Init',imgdim(3),'reslicing...','planes completed');
for i = 1:imgdim(3)
M = inv(spm_matrix([0 0 -i])*inv(VO.mat)*V.mat);
img = spm_slice_vol(V, M, imgdim(1:2), 1); % (linear interp)
if ismask
img = round(img);
end
spm_write_plane(VO, img, i);
spm_progress_bar('Set', i)
end
spm_progress_bar('Clear');
end
% call spm_close_vol if spm2
if ~spm5
spm_close_vol(VO);
end
if (isempty(Fint) && ~isempty(Fnew))
% interactive figure was opened by this script, so close it again.
close(Fnew);
end
disp('Done.')
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function bb = world_bb(V)
% world-bb -- get bounding box in world (mm) coordinates
d = V.dim(1:3);
% corners in voxel-space
c = [ 1 1 1 1
1 1 d(3) 1
1 d(2) 1 1
1 d(2) d(3) 1
d(1) 1 1 1
d(1) 1 d(3) 1
d(1) d(2) 1 1
d(1) d(2) d(3) 1 ]';
% corners in world-space
tc = V.mat(1:3,1:4)*c;
% reflect in x if required
if spm_flip_analyze_images; tc(1,:) = -tc(1,:); end;
% bounding box (world) min and max
mn = min(tc,[],2)';
mx = max(tc,[],2)';
bb = [mn; mx];
}}}
NOTE: [[resize_img]] by Ged Ridgway works better.
{{{
function reslice(PI,PO,dim,mat,hld)
% RESLICE Reslices an image file
% RESLICE(PI,PO,dim,mat,hld)
% PI - input filename
% PO - output filename
% dim - 1x3 matrix of image dimensions
% mat - 4x4 affine transformation matrix mapping
% from vox to mm (for output image).
% To define M from vox and origin, then
% off = -vox.*origin;
% M = [vox(1) 0 0 off(1)
% 0 vox(2) 0 off(2)
% 0 0 vox(3) off(3)
% 0 0 0 1];
% How do I get this information from the image to be resliced? It can
% be extracted from the original image mat field.
% newmat = spm_imatrix(original_mat);
% newmat(1:3) now contains the offsets (off) and newmat(7:9) are
% the voxel sizes. Conversely the other values in newmat can just be
% set to 0, as in: newmat([4:6,10:12]) = 0;
% and the transformation matrix re-assembled.
% newmat = spm_matrix(newmat);
%
% hld - interpolation method.
% 0 = nearest neighbor
% 1 = trilinear
% 2 - 7 = various levels of b-splines. Do not use b-splines if there
% are NaN's in the data.
% @(#)JohnsGems.html 1.41 John Ashburner 04/07/05
% Modified by D. Gitelman to add an interface use mldivide.
% $Id: reslice.m,v 1.4 2006-11-22 03:59:16-06 drg Exp drg $
% check for SPM-related defaults as a global.
defchk = whos('global');
VI = [];
n = nargin;
spmver = lower(spm('ver'));
while n < 5
if isempty(VI)
try
VI = spm_vol(PI);
catch
% if PI doesn't exist, we end up here which is OK, 'cause
% it gets filled in next time. Once PI is mapped to VI we
% don't need to redo it.
end
end
switch n
case 0
switch spmver
case 'spm2'
PI = spm_get(1,'image','Select image for reslicing');
case 'spm5'
PI = spm_select(1,'image','Select image for reslicing');
end
n = n + 1;
case 1
[pth fn ext] = fileparts(PI);
PO = fullfile(pth,['rs_',fn,'.img']);
n = n + 1;
case 2
VO.fname = PO;
dim = VI.dim(1:3);
n = n + 1;
case 3
switch spmver
case 'spm2'
if isfield(VI,'dt')
type = VI.dt(1);
elseif length(VI.dim) == 4
type = VI.dim(4);
else
error('Unknown image file type.')
end
dim = [dim, type];
VO.dim = dim;
case 'spm5'
if isfield(VI,'dt')
VO.dt = VI.dt;
elseif length(VI.dim) == 4
type = VI.dim(4);
else
error('Unknown image file type.');
end
VO.dim = dim;
VO.dt = VI.dt;
end
mat = VI.mat;
n = n + 1;
case 4
% create a matrix for the resliced volume (can be read by
% MRICRO)
newmat = mat;
newmat = spm_imatrix(newmat);
newmat([4:6,10:12]) = 0;
newmat = spm_matrix(newmat);
VO.mat = newmat;
hld = 1;
n = n + 1;
end
end
VO.descrip = ['resliced, ',VI.descrip];
VO.pinfo = [1 0 0]';
% Other private and/or NIFTI related fields will get filled in
% automatically when the volume is created.
% create the empty volume.
switch spmver
case 'spm2'
VO = spm_create_vol(VO);
case 'spm5'
VO = spm_create_vol(VO);
end
drawnow
for x3 = 1:VO.dim(3),
fprintf('Writing plane %3i',x3)
% Use mldivide to solve for the transformation matrix that will
% eliminate rotations, shears and zooms. Translations are still
% required because we have a non-zero origin. mldivide is faster than
% inv.
M = inv(spm_matrix([0 0 -x3 0 0 0 1 1 1])*(VO.mat\VI.mat));
v = spm_slice_vol(VI,M,VO.dim(1:2),hld);
VO = spm_write_plane(VO,v,x3);
fprintf(repmat(sprintf('\b'),1,17));
end;
fprintf('Done\n');
return;
}}}
{{{
function fxnVer = retVer(fname)
% RETVER returns the source code control version string from a file
% FXNVER = RETVER(FNAME) returns the version string from a file that is
% under source code control. The function assume the version string
% starts and ends with the character '$'. FNAME is the name of a file
% in the current directory or a fully qualified path and filename.
% Author: Darren Gitelman
% $Id: retVer.m,v 1.1 2007-08-06 18:16:50-05 drg Exp drg $
% open the file
fid = fopen(fname);
if fid < 1
error('Could not open file.');
end
% read the string. Discard everything up to the first $, then keep the rest
% of that line.
textscan(fid,'%*[^$]');
str = fgetl(fid);
fclose(fid);
% remove trailing spaces and check that the string is not empty.
str = deblank(str);
if isempty(str)
error('Version string not found.');
end
% return the string.
fxnVer = str;
}}}
This functions allows setting the values in a selected slab 'o slices to 0. This is useful, for example, to remove regions of noise outside the brain or to remove the neck when images are acquired sagittally.
To use this function, make a directory named setslices2zero in the SPM5 toolbox directory. Save this function as a file called ''cbmg_config_setslices2zero.m'' in this directory.
{{{
function unwrap = cbmg_config_setslices2zero
% CBMG_CONFIG_ SETSSLICES2ZERO sets the values in selected slices to 0.
% This function returns a job structure for SPM5, and runs the job when
% called by spm_jobman.
%
% Portions of this code are derived from Volkmar Glauche's
% tbxvol_unwrap and vgtbx_config_Volumes functions.
% Author: Darren Gitelman
% $Id: cbmg_config_setslices2zero.m,v 1.0 2006-11-28 09:37:50-06 drg Exp drg $
addpath(fullfile(spm('dir'),'toolbox','setslices2zero'));
srcimg.type = 'files';
srcimg.name = 'Source image';
srcimg.tag = 'srcimg';
srcimg.filter = 'image';
srcimg.num = [1 1];
srcimg.help = {'Select the volume to unwrap.'};
startSlice.type = 'entry';
startSlice.name = 'First slice in slab';
startSlice.tag = 'startSlice';
startSlice.num = [1 1];
startSlice.strtype = 'i';
startSlice.help = {'Starting slice number. This must be less than the end slice.'};
endSlice.type = 'entry';
endSlice.name = 'Last slice in slab';
endSlice.tag = 'endSlice';
endSlice.num = [1 1];
endSlice.strtype = 'i';
endSlice.help = {'Ending slice number. This must be greater than the start slice.'};
planedir.type = 'menu';
planedir.name = 'Slice plane orientation (voxel space)';
planedir.tag = 'planedir';
planedir.labels = {'X', 'Y', 'Z'};
planedir.values = {1, 2, 3};
planedir.help = {'Orientation of the slice plane in voxel space.'};
prefix.type = 'entry';
prefix.name = 'Output filename prefix';
prefix.tag = 'prefix';
prefix.val = {'z'};
prefix.strtype = 's';
prefix.num = [1 Inf];
prefix.help = {['The output filename is constructed by prepending the original filename ' ...
'with this prefix.']};
unwrap.type = 'branch';
unwrap.name = 'Set Slices to Zero';
unwrap.tag = 'zeroslices';
unwrap.prog = @zeroSlices;
unwrap.val = {srcimg startSlice endSlice planedir prefix};
unwrap.help = {'This function allows setting the values in a plane of slices to 0.'};
% ===================================================================
function zeroSlices(varargin)
job = varargin{1};
for k=1:numel(job.srcimg)
V = spm_vol(job.srcimg{k});
X = spm_read_vols(V);
switch(job.planedir)
case 1,
X(job.startSlice:job.endSlice,:,:) = 0;
case 2,
X(:,job.startSlice:job.endSlice,:) = 0;
case 3,
X(:,:,job.startSlice:job.endSlice) = 0;
end;
Vunwrap = V;
[p n e v] = fileparts(Vunwrap.fname);
Vunwrap.fname = fullfile(p, [job.prefix n e v]);
Vunwrap = spm_create_vol(Vunwrap);
Vunwrap = spm_write_vol(Vunwrap,X);
spm_check_registration([V,Vunwrap])
end;
}}}
{{{
function varargout = smoothspmimage(vin,th,fwhm,vx,vout)
% SMOOTHSPMIMAGE smooths an spm generated analyze image
% IMG = SMOOTHSPMIMAGE(VIN,TH,FWHM,VOUT) smooths an SPM-generated image,
% which may contain NaN's or zeros while controlling edge effects. VIN
% and VOUT can be filenames (VIN must exist, VOUT should not),
% volume structures or images. If nargout == 1 and VOUT is empty then
% the smoothed volume is returned as an output and a file is not
% written. TH is the threshold used to mask the volume, a filename or
% volume structure of an image mask, or an image mask that must be the
% same size as the volume defined by VIN. FWHM is the FWHM in mm. If
% FWHM is a scalar the smoothing is assumed to be isotropic. VX must be
% supplied if VIN is an image.
%
% VIN, TH AND FWHM must be supplied.
%
% Based on example code provided by Christian Gaser on the SPM list
% 2006-12-27
% Author: Darren Gitelman
% $Id: smoothspmimage.m,v 1.0 2006-12-27 18:05:48-06 drg Exp drg $
% check arguments
if nargin < 3
error('Must provide VIN, TH, AND FWHM.')
end
try
spmver = spm('ver');
catch
error('SPM must be in the Matlab path.');
end
if ~strcmpi(spmver,'SPM5')
error('SMOOTHSPMIMAGE requires SPM5.')
end
varargout = {};
% is vin a filename?
if ischar(vin) && (exist(vin,'file') == 2)
vin = spm_vol(vin);
vol = spm_read_vols(vin);
% or is vin a volume structure?
elseif isstruct(vin)
vol = spm_read_vols(vin);
elseif isnumeric(vin)
if (nargin >= 4) && (isnumeric(vx) & (numel(vx) == 3))
vol = vin;
else
error('VX must be supplied if VIN is an image.')
end
else
error('VIN must be a filename, a volume structure or an image.');
end
% is vout a filename?
if nargin == 5
if ischar(vout) && ~isempty(vout)
vouttmp = vin;
vouttmp.fname = vout;
vout = vouttmp;
% or is it an empty string?
elseif ischar(vout) && sempty(vout)
vout = [];
% if it isn't a volume structure then we don't know what it is.
elseif ~isstruct(vout)
error('VOUT must be a filename, a volume structure or empty.')
end
else
vout = [];
end
% is th a filename?
if ischar(th) && (exist(th,'file') == 2)
vth = spm_vol(th);
mask = spm_read_vols(vth);
% is th a volume structure?
elseif isstruct(th)
mask = spm_read_vols(th);
% perhaps th is an image
elseif isnumeric(th) && (numel(th) > 1)
mask = th;
% or is th a scalar
elseif isnumeric(th) && (numel(th) == 1)
mask = vol > th;
else
error('TH must be a filename, a volume structure, an image or a scalar.')
end
% check FWHM
if (numel(fwhm) == 1)
fwhm = repmat(fwhm,1,3);
else
fwhm = fwhm(:)';
end
% get voxel size
if nargin < 4
vx = sqrt(sum(vin.mat(1:3,1:3).^2));
end
% get dim
dim = size(vol);
% do the work
Q = find(mask > 0);
% setup volumes
svol = zeros(dim);
smask = zeros(dim);
tmpvol = zeros(dim);
tmpvol(Q) = ones(size(Q));
% smooth tmpvol which is actually the mask and return in smask
spm_smooth(tmpvol,smask,fwhm./vx);
% now set tmpvol = vol (but only where mask is >0)
tmpvol(Q) = vol(Q);
% smooth tmpvol and return in svol
spm_smooth(tmpvol,svol,fwhm./vx);
% vol becomes svol but only where smask is > 0 where mask > 0.
vol(Q) = svol(Q)./smask(Q);
% if function was called with an output argument return it
if nargout == 1
varargout{1} = vol;
end
% if vout is not empty write out the image.
if ~isempty(vout)
spm_write_vol(vout,vol);
end
}}}
{{{
function sortfiles()
% SORTFILES sorts a set of analyze/DICOM files into named directories.
% SORTFILES assumes that the files are named according to the SPM DICOM
% conversion convention:
% [f or s]subjectID-SeriesNumber-InstanceNumber-AcquisisionNumber.[hdr|img|mat]
%
% The base directory is the directory that will contain the new
% directories that hold the files.
%
% The number of runs should include the T1 volume.
%
% The series number is critical for selecting the correct images. Do not
% include any leading zeros.
%
% Directory name is the name of the directory the files will be saved
% in. The default is run#.
%
% The function currently only works in SPM2.
% Authors: Darren Gitelman
% $Id: sortfiles.m,v 1.0 2006-08-30 01:59:32-05 drg Exp drg $
% Clear the interactive window
Finter = spm_figure('FindWin','Interactive');
spm_clf(Finter)
% select the base directory for the files
basedir = (spm_get(-1,'*','Select the base directory',pwd));
cd(basedir)
currdir = pwd;
% specify the number of conditions. Do NOT Include the T1
ndir = spm_input('Number of runs (incl. T1)','1','n','1',1);
cond = cell(ndir,2);
% Get the conditions and names from the user
for i = 1:ndir
cond{i,1} = spm_input(['Enter series number for run ',num2str(i),':'],...
1,'n','1',1);
cond{i,2} = spm_input(['Num. of dummies for series ',num2str(cond{i,1}),':'],...
1,'n','4',1);
cond{i,3} = spm_input(['Directory name for series ',num2str(cond{i,1}),':'],...
'+1','s',['run_',num2str(i)]);
end
% Identify the files.
P = spm_get(Inf,'IMAGE','Select all image files',[],0);
% parse the file names into series and image numbers
dummy = [];
ser = zeros(size(P,1));
img = zeros(size(P,1));
for j = 1:size(P,1)
[dummy, ser(j), img(j), dummy] = strread(P(j,:),'%s%d%d%d','delimiter','-');
end
% make delete directory
[MKDIRSUCCESS,MKDIRMESS,MKDIRMESSID] = mkdir('DELETEME');
if ~MKDIRSUCCESS
fprintf('%s\n',MKDIRMESS)
error('Problem Making Directory. See error above.')
end
spm_progress_bar('Init',size(cond,1),'Moving Files','Series completed')
for i = 1:size(cond,1)
% Make the new directory
[MKDIRSUCCESS,MKDIRMESS,MKDIRMESSID] = mkdir(cond{i,3});
if ~MKDIRSUCCESS
fprintf('%s\n',MKDIRMESS)
error('Problem Making Directory. See error above.')
end
% find the files corresponding of the series of interest
idx = find(ser == cond{i,1});
% get the image numbers of the series
imgnum = img(idx);
% sort the image numbers
[p,q] = sort(imgnum);
deletefn = [];
% find the dummies
if cond{i,2} ~= 0
deletefn = P(idx(q(1:cond{i,2})),:);
end
dashidx = findstr(P(idx(1),:),'-');
% put dummies into a deleteme directory
for j = 1:size(deletefn,1)
protofn = deletefn(j,1:dashidx(end));
movefile([protofn,'*'],'DELETEME')
end
% move the remaining files in the series into their directories
% it is assumed that the file names up through the series number
% are all identical
protofn = P(idx(1),1:dashidx(end-1));
[FILESUCCESS,FILESUCCESSMESS,FILESUCCESSMESSID] = ...
movefile([protofn,'*'],cond{i,3});
spm_progress_bar('Set',i)
end
spm_progress_bar('Clear')
spm_clf(Finter)
set(Finter,'Name','DONE')
}}}
See UnwrapSlabInstructions for instructions and an example of how to use this function.
To use this function, make a directory named unwrapslab in the SPM5 toolbox directory. Save this function as a file called ''cbmg_config_unwrapslab''.m in this directory.
{{{
function unwrap = cbmg_config_unwrapslab
% CBMG_CONFIG_UNWRAPSLAB unwraps a user selected slab 'o slices.
% This function returns a job structure for SPM5, and runs the job when
% called by spm_jobman.
%
% Portions of this code are derived from Volkmar Glauche's
% tbxvol_unwrap and vgtbx_config_Volumes functions.
% Author: Darren Gitelman
% $Id: cbmg_config_unwrapslab.m,v 1.3 2006-11-28 09:20:44-06 drg Exp drg $
addpath(fullfile(spm('dir'),'toolbox','unwrapslab'));
srcimg.type = 'files';
srcimg.name = 'Source image';
srcimg.tag = 'srcimg';
srcimg.filter = 'image';
srcimg.num = [1 1];
srcimg.help = {'Select the volume to unwrap.'};
cor_orig.type = 'menu';
cor_orig.name = 'Keep position of wrapped images';
cor_orig.tag = 'cor_orig';
cor_orig.labels = {'Yes', 'No'};
cor_orig.values = {1, 0};
cor_orig.help = {['Choosing yes will shift the volume backwards or ',...
'or fowards by the size of the slab. This acts to maintain the same ',...
'relative origin position as in the original image.']};
startSlice.type = 'entry';
startSlice.name = 'First slice in slab';
startSlice.tag = 'startSlice';
startSlice.num = [1 1];
startSlice.strtype = 'i';
startSlice.help = {'Starting slice number. This must be less than the end slice.'};
endSlice.type = 'entry';
endSlice.name = 'Last slice in slab';
endSlice.tag = 'endSlice';
endSlice.num = [1 1];
endSlice.strtype = 'i';
endSlice.help = {'Ending slice number. This must be greater than the start slice.'};
wrapdir.type = 'menu';
wrapdir.name = 'Wrap direction (voxel space)';
wrapdir.tag = 'wrapdir';
wrapdir.labels = {'X', 'Y', 'Z'};
wrapdir.values = {1, 2, 3};
wrapdir.help = {'Direction in which to wrap the slices.'};
prefix.type = 'entry';
prefix.name = 'Output filename prefix';
prefix.tag = 'prefix';
prefix.val = {'U'};
prefix.strtype = 's';
prefix.num = [1 Inf];
prefix.help = {['The output filename is constructed by prepending the original filename ' ...
'with this prefix.']};
unwrap.type = 'branch';
unwrap.name = 'Unwrap Slab';
unwrap.tag = 'unwrap';
unwrap.prog = @slabUnwrap;
unwrap.val = {srcimg cor_orig startSlice endSlice wrapdir prefix};
unwrap.help = {[...
'This function allows the unwrapping of a slab of slices. ',...
'This may be necessary if blank slices are interposed between the ',...
'end of a volume and the slices to be wrapped. The function assumes ',...
'that slices less than the middle slice in the unwrap direction ',...
'should be moved backward, and slices greater than the middle slice ',...
'in the unwrap direction should be moved forward.'],...
[''],...
['The slices to be unwrapped can be figured out by displaying the ',...
'image using the SPM display button. Then choose "voxel space" ',...
'as the reference frame. The directions are as follows:'],...
['lower left image: columns = X and rows = Y '],...
['upper left image: columns = X and rows = Z '],...
['upper right image: columns = Y and rows = Z.']};
% ===================================================================
function slabUnwrap(varargin)
job = varargin{1};
for k=1:numel(job.srcimg)
V = spm_vol(job.srcimg{k});
Mc=eye(4);
if job.cor_orig
if (job.startSlice < V.dim(job.wrapdir)/2)
Mc(job.wrapdir,4) = (job.endSlice-job.startSlice);
else
Mc(job.wrapdir,4) = -(job.endSlice-job.startSlice);
end
end;
X = spm_read_vols(V);
switch(job.wrapdir)
case 1,
slices = [1:V.dim(1)];
slices(job.startSlice:job.endSlice) = [];
if (job.startSlice < V.dim(1)/2)
idx = [slices, job.startSlice:job.endSlice];
else
idx = [job.startSlice:job.endSlice, slices];
end
X1 = X(idx,:,:);
case 2,
slices = [1:V.dim(2)];
slices(job.startSlice:job.endSlice) = [];
if (job.startSlice < V.dim(2)/2)
idx = [slices, job.startSlice:job.endSlice];
else
idx = [job.startSlice:job.endSlice, slices];
end
X1 = X(:,idx,:);
case 3,
slices = [1:V.dim(3)];
slices(job.startSlice:job.endSlice) = [];
if (job.startSlice < V.dim(3)/2)
idx = [slices, job.startSlice:job.endSlice];
else
idx = [job.startSlice:job.endSlice, slices];
end
X1 = X(:,:,idx);
end;
Vunwrap = V;
Vunwrap.mat=Vunwrap.mat*Mc;
[p n e v] = fileparts(Vunwrap.fname);
Vunwrap.fname = fullfile(p, [job.prefix n e v]);
Vunwrap = spm_create_vol(Vunwrap);
Vunwrap = spm_write_vol(Vunwrap,X1);
spm_check_registration([V,Vunwrap])
end;
}}}
This function has been superceded by the [[unwrapSlab]] function. (2006-11-25)
{{{
function unwrapping(varargin)
% UNWRAPPING Unwraps MRI images
% UNWRAPPING(FNAME,LIM,DIM) moves image data of file FNAME using limits
% LIM defining the start and end voxels of the slab to move.
% Movement is along the DIM dimension. The program depends on the
% wrapped bit of brain being located on an edge, which should always be
% the case. If the necessary arguments are not specified, the user is
% prompted for them.
%
% Only one image may be processed at a time.
%
% The program is very simple minded. You must figure out the voxels to
% move and the correct dimension along which to shift the slab. If you
% choose badly you get bupkiss. The good news is that the original
% volume is untouched and you can try again.
% Authors: Darren Gitelman
% $Id: unwrapping.m,v 1.1 2006-08-31 00:58:02-05 drg Exp drg $
drawnow;
% check the inputs.
switch nargin
case 0
fname = my_selectVol;
unwrapping(fname);
return
case 1
lim = spm_input('Limits of slab (min max) [voxels].', '+1', 'e', [], 2);
unwrapping(varargin{1},lim)
return
case 2
dim = spm_input('Choose dimension to shift slab','+1','m',...
'Front->Back|Side->Side|Top->Bottom',[2 1 3],1);
unwrapping(varargin{1},varargin{2},dim);
return
case 3
% do nothing to proceed with analysis.
otherwise
error('Incorrect number of input arguments.')
end
fname = varargin{1};
lim = varargin{2};
lim = sort(lim);
dim = varargin{3};
% get the image name and header info.
% if using spm2 substitute spm_get for spm_select
% Select 1 file of type image.
v = spm_vol(fname);
% read image data into matrix 'img'
% don't leave off semicolon or you'll have to watch a lot of numbers!
img = spm_read_vols(v);
% display a slice of the image. The image should be 3 dimensional.
% DIM 1 = X, DIM 2 = Y, and DIM 3 = Z
f = findobj('Tag','UnwrappingFig');
if isempty(f) || ~ishandle(f)
f = figure('Tag','UnwrappingFig');
end
figure(f);
set(f,'color','w');
clf
colormap(gray)
subplot(1,2,1)
switch dim
case {1,2}
imagesc(img(:,:,round(size(img,3)/2)))
case 3
imagesc(img(round(size(img,1)/2),:,:))
otherwise
error('bad dim value')
end
title('Before')
axis image off
drawnow
% for reasons that are not clear the X axis is where the Y axis should be
% and vice versa. Usually you will want to move the back of the image to
% front. If the image appears as an axial slice you will be working on the
% Y axis (DIM 2). If the image appears as an sagittal slice you will be
% working on the X axis (DIM 1). I don't know about coronal slices but I
% presume is would be the Z axis.
% Let's assume we're working on an axial slice.
% Look at the displayed image and decide where the wrapped piece ends. Add
% a few more voxels to give yourself some cushion. For example, if the
% wrapped piece is from 245 - 256 then you will end up moving 235 - 256.
% Just make sure voxel 235-245 does not contain the front part of the
% brain. Remember you are working in VOXELS not MM.
% Now assemble the new image.
% The cat command is invaluable for making sure you put the correct
% dimensions together. In this case we want to join along dimension 2. If
% the image was sagittal it would be dimension 1.
switch dim
case 1
imgOut = cat(1,img(lim(1):lim(2),:,:),img(1:lim(1)-1,:,:));
case 2
imgOut = cat(2,img(:,lim(1):lim(2),:),img(:,1:lim(1)-1,:));
case 3
imgOut = cat(3,img(:,:,lim(1):lim(2),:),img(:,:,1:lim(1)-1));
otherwise
error('Bad dim value.');
end
% break the file name down into parts
[pth, fn, ext, vs]=fileparts(v.fname);
% change the file name.
fn = ['nowrap',fn];
% reassemble the complete filename
v.fname = fullfile(pth,[fn,ext]);
% create the new file and write the data to it.
% MAKE SURE YOU HAVE CHANGED THE FILE NAME OR YOU WILL OVERWRITE
% THE OLD FILE.
spm_write_vol(v,imgOut);
% Look at the image to make sure we did it correctly.
subplot(1,2,2)
switch dim
case {1,2}
imagesc(imgOut(:,:,round(size(imgOut,3)/2)))
case 3
imagesc(imgOut(round(size(imgOut,1)/2),:,:))
otherwise
error('Bad dim value.')
end
title('After')
axis image off
drawnow
fprintf('Done. The new file is %s\n',v.fname)
end
function fname = my_selectVol()
% choose a volume to process
fname = cbmggetselect(1,'image','Select an MRI volume to process');
end
}}}
{{{
function out = vecdist(P1,P2)
% VECDIST Euclidean distance between two vectors.
% D = VECDIST([X1 Y1 Z1],[X2 Y2 X2]) returns the Euclidean distance
% between two vectors using the standard formula.
% sqrt( (X2-X1)^2 + (Y2-Y1)^2 + (Z2-Z1)^2 )
% Darren Gitelman
% $Id: vecdist.m,v 1.1 2006-08-15 21:38:50-05 drg Exp drg $
if nargin ~= 2
error('Must use 2 and only 2 vectors.')
out = NaN;
return
end
% orient both vectors the same way
P1 = P1(:);
P2 = P2(:);
% in the interest of simplicity.
out = sqrt( (P2(1)-P1(1))^2 + (P2(2)-P1(2))^2 + (P2(3)-P1(3))^2 );
return
}}}