├── README.md ├── pom.xml └── src └── main ├── java └── de │ └── embl │ └── cmci │ └── registration │ ├── Compress_Matrices.java │ ├── MultiStackRegCredits.java │ └── MultiStackReg_.java └── resources └── plugins.config /README.md: -------------------------------------------------------------------------------- 1 | MultiStackRegistration 2 | ====================== 3 | 4 | Modified version of MultiStackRegistration, for use from scripts. 5 | 6 | ## Authors 7 | 8 | * Brad Busse (Designer, Developer, - v 1.45) 9 | * bbusse@stanford.edu 10 | * Kota Miura (Minor upgrades, v1.46) 11 | * Mavenized, Scripting enabled, bug fixing. 12 | * miura@cmci.info 13 | 14 | ## Installation 15 | 16 | Please add the plugin "MultistackReg" using Update Sites function in Fiji. 17 | 18 | * briefly, [help > Update...], then in the Updater window, click "Manage Update Site", and tick "MultiStackReg" (also tick "BIG_EPFL" for the dependency) - close the window, click "Apply Changes". When downloading is done, please restart Fiji. 19 | 20 | ## Dependencies 21 | 22 | * ImageJ 23 | * 24 | * TurboReg 25 | * 26 | * TurboReg is included in the UpdateSite package from "BIG_EPFL", so please simply add the site to your Fiji installation. 27 | 28 | ## Usage 29 | 30 | Welcome to MultiStackReg v1.5! 31 | 32 | This plugin has three modes of use: 33 | 34 | 1. Align a single stack of images 35 | 2. Align a stack to another stack 36 | 3. Load a previously created transformation file 37 | 38 | ### To align a single stack: 39 | 40 | Choose the image to be aligned in the Stack 1 dropdown box. 41 | Leave 'Align' in the Action 1 box. 42 | 43 | ### To align two stacks: 44 | 45 | Place the reference stack in Stack 1's box, and the stack to be aligned in Stack 2's box. Select 'Use as Reference' as Action 1 and 'Align to First Stack' as Action 2. 46 | 47 | ### To load a transformation file: 48 | 49 | Place the stack to be aligned in either stack box, choose 'Load Transformation File' in its Action dropdown box. The File field can be used as a shortcut to avoid having to select the matrix manually. 50 | 51 | ### Javadoc 52 | 53 | http://wiki.cmci.info/javaapi/multiStackReg_/ 54 | 55 | ## Forum 56 | 57 | See the thread below in the Forum for discussions, macro usages, and Q&As. 58 | 59 | https://forum.image.sc/t/multistackreg-macro/174 60 | 61 | ## Credits: 62 | 63 | This work is based on the following paper: 64 | 65 | P. Thévenaz, U.E. Ruttimann, M. Unser 66 | A Pyramid Approach to Subpixel Registration Based on Intensity 67 | IEEE Transactions on Image Processing 68 | vol. 7, no. 1, pp. 27-41, January 1998. 69 | 70 | This paper is available on-line at 71 | 72 | * 73 | 74 | Other relevant on-line publications are available at 75 | 76 | * 77 | 78 | Additional help available at 79 | 80 | * 81 | 82 | Ancillary TurboReg_ plugin available at 83 | 84 | * 85 | 86 | You'll be free to use this software for research purposes, but you should not redistribute it without our consent. In addition, we expect you to include a citation or acknowledgment whenever you present or publish results that are based on it. 87 | 88 | A few changes (loadTransform, appendTransform, multi stack support) to support load/save functionality and multiple stacks were added by Brad Busse ( bbusse@stanford.edu ) and released into the public domain, so go by their ^^ guidelines for distribution, etc. 89 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | org.scijava 16 | pom-scijava 17 | 26.0.0 18 | 19 | 20 | 21 | de.embl.cmci 22 | MultiStackRegistration_ 23 | 1.46.5 24 | 25 | plugins/MultiStackRegistration_.jar 26 | A Maven project implementing an ImageJ 1.x plugin. MultiStackRegistration plugin written by Brad Busse. 27 | https://github.com/miura/MultiStackRegistration 28 | 2010 29 | 30 | Fiji 31 | https://fiji.sc 32 | 33 | 34 | 35 | None 36 | 37 | 38 | 39 | 40 | 41 | scm:git:git://github.com/miura/MultiStackRegistration 42 | scm:git:git@github.com:miura/MultiStackRegistration 43 | HEAD 44 | https://github.com/miura/MultiStackRegistration 45 | 46 | 47 | GitHub Issues 48 | https://github.com/miura/MultiStackRegistration/issues 49 | 50 | 51 | 52 | 56 | 57 | scijava.public 58 | https://maven.scijava.org/content/groups/public 59 | 60 | 61 | 62 | 63 | net.imagej 64 | ij 65 | 66 | 67 | sc.fiji 68 | TurboReg_ 69 | 2.0.0 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.codehaus.mojo 78 | exec-maven-plugin 79 | 80 | 81 | 82 | java 83 | 84 | 85 | 86 | 87 | MultiStackRegistration_ 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-javadoc-plugin 93 | 94 | ${javadoc.skip} 95 | 96 | 97 | 98 | attach-javadocs 99 | 100 | jar 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Brad Busse 111 | bbusse@stanford.edu 112 | http://bradbusse.net/downloads.html 113 | 114 | developer 115 | 116 | 117 | 118 | 119 | kota 120 | Kota Miura 121 | miura@embl.de 122 | http://cmci.embl.de/ 123 | EMBL Heidelberg 124 | http://www.embl.de/ 125 | 126 | developer 127 | 128 | +1 129 | 130 | 131 | 132 | 133 | None 134 | 135 | 136 | 137 | 138 | Image.sc Forum 139 | https://forum.image.sc/tags/fiji 140 | 141 | 142 | 143 | None 144 | 145 | 146 | 147 | 148 | true 149 | 1.8 150 | N/A 151 | N/A 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /src/main/java/de/embl/cmci/registration/Compress_Matrices.java: -------------------------------------------------------------------------------- 1 | package de.embl.cmci.registration; 2 | 3 | import ij.IJ; 4 | import ij.gui.GenericDialog; 5 | import ij.plugin.PlugIn; 6 | import java.awt.FileDialog; 7 | import java.awt.Frame; 8 | import java.io.FileReader; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileWriter; 11 | import java.io.BufferedReader; 12 | import java.io.IOException; 13 | import java.lang.Comparable; 14 | import java.util.SortedSet; 15 | import java.util.TreeSet; 16 | import java.util.Iterator; 17 | 18 | import java.lang.String; 19 | 20 | public class Compress_Matrices implements PlugIn { 21 | 22 | private String loadPathA; 23 | private String loadPathB; 24 | private String savePath; 25 | 26 | private double[][] srcPtsA = new double[3][2]; 27 | private double[][] srcPtsB = new double[3][2]; 28 | private double[][] tgtPtsA = new double[3][2]; 29 | private double[][] tgtPtsB = new double[3][2]; 30 | 31 | //the set of points to use when squishing and output 32 | private double[][] gblPts = new double[3][2]; 33 | private boolean gblPtsInit; 34 | 35 | private SortedSet listA = new TreeSet(); 36 | private SortedSet listB = new TreeSet(); 37 | 38 | private int globalIndex; 39 | 40 | public void run (final String arg) { 41 | loadPathA=""; 42 | loadPathB=""; 43 | savePath=""; 44 | 45 | Runtime.getRuntime().gc(); 46 | 47 | GenericDialog gd = new GenericDialog("Compress Matrices"); 48 | gd.addMessage("Please select two array files in the order you want them applied."); 49 | 50 | gd.showDialog(); 51 | if (gd.wasCanceled()) { 52 | return; 53 | } 54 | gblPtsInit=false; 55 | final Frame t = new Frame(); 56 | final FileDialog fl = new FileDialog( t, "Load first transformation file", FileDialog.LOAD); 57 | fl.setVisible(true); 58 | if (fl.getFile() == null){ 59 | IJ.error("Action cancelled"); 60 | return; 61 | } 62 | loadPathA = fl.getDirectory()+fl.getFile(); 63 | processFile("A"); 64 | 65 | 66 | //final Frame t2 = new Frame(); 67 | final FileDialog f2 = new FileDialog( t, "Load second transformation file", FileDialog.LOAD); 68 | f2.setVisible(true); 69 | if (f2.getFile() == null){ 70 | IJ.error("Action cancelled"); 71 | return; 72 | } 73 | loadPathB = f2.getDirectory()+f2.getFile(); 74 | processFile("B"); 75 | 76 | //final Frame t3 = new Frame(); 77 | final FileDialog f3 = new FileDialog( t, "Save new transformation file", FileDialog.SAVE); 78 | f3.setVisible(true); 79 | if (f3.getFile() == null){ 80 | IJ.error("Action cancelled"); 81 | return; 82 | } 83 | savePath = f3.getDirectory()+f3.getFile(); 84 | //processFile("B"); 85 | squishArrays(); 86 | 87 | } /* end run */ 88 | 89 | public void squishArrays(){ 90 | Iterator itA = listA.iterator(); 91 | Iterator itB = listB.iterator(); 92 | listElement currA; 93 | listElement currB=null; 94 | 95 | startWriting(); 96 | if (itB.hasNext()) 97 | currB= (listElement)itB.next(); 98 | while (itA.hasNext()){ 99 | currA= (listElement)itA.next(); 100 | while(currB != null && currA.compareTo(currB)>0){ 101 | keepWriting(currB); 102 | if (itB.hasNext()) 103 | currB= (listElement)itB.next(); 104 | else 105 | currB = null; 106 | } 107 | 108 | if (currB != null && currA.compareTo(currB)<0){//A comes before B, so write A and keep going 109 | keepWriting(currA); 110 | }else if (currB != null && currA.compareTo(currB)==0){//A==B. Squish them together and write the result 111 | //SQUISH 112 | keepWriting(sliceSquish(currA,currB)); 113 | if (itB.hasNext()) 114 | currB= (listElement)itB.next(); 115 | else 116 | currB= null; 117 | }else{ 118 | keepWriting(currA); 119 | } 120 | } 121 | if (currB != null) keepWriting(currB); 122 | while(itB.hasNext()){ 123 | currB= (listElement)itB.next(); 124 | keepWriting(currB); 125 | } 126 | } 127 | 128 | public listElement sliceSquish(listElement oA,listElement oB){ 129 | /* 130 | Take the effect that matrix A has on a set of constants, run that through 131 | matrix B, then use the result to derive a new matrix that does both at once. 132 | */ 133 | double[][] m; 134 | double[][] m1,m2; 135 | double[][] pts=new double[3][2]; 136 | double[][] pts2=new double[3][2]; 137 | 138 | m=oA.matrix; 139 | pts[0][0]=(m[0][0]*gblPts[0][0] + m[0][1]*gblPts[0][1] + m[0][2]); 140 | pts[0][1]=(m[1][0]*gblPts[0][0] + m[1][1]*gblPts[0][1] + m[1][2]); 141 | pts[1][0]=(m[0][0]*gblPts[1][0] + m[0][1]*gblPts[1][1] + m[0][2]); 142 | pts[1][1]=(m[1][0]*gblPts[1][0] + m[1][1]*gblPts[1][1] + m[1][2]); 143 | pts[2][0]=(m[0][0]*gblPts[2][0] + m[0][1]*gblPts[2][1] + m[0][2]); 144 | pts[2][1]=(m[1][0]*gblPts[2][0] + m[1][1]*gblPts[2][1] + m[1][2]); 145 | 146 | //These strings are very useful for debugging, though the pts calc does clog the screen 147 | String s=oA.index+" "+oB.index+"\n"+oA.index+":First transformation\n"+pts[0][0]+" "+pts[0][1]+"\n"+pts[1][0]+" "+pts[1][1]+"\n"+pts[2][0]+" "+pts[2][1]; 148 | 149 | m=oB.matrix; 150 | pts2[0][0]=(m[0][0]*pts[0][0] + m[0][1]*pts[0][1] + m[0][2]); 151 | pts2[0][1]=(m[1][0]*pts[0][0] + m[1][1]*pts[0][1] + m[1][2]); 152 | pts2[1][0]=(m[0][0]*pts[1][0] + m[0][1]*pts[1][1] + m[0][2]); 153 | pts2[1][1]=(m[1][0]*pts[1][0] + m[1][1]*pts[1][1] + m[1][2]); 154 | pts2[2][0]=(m[0][0]*pts[2][0] + m[0][1]*pts[2][1] + m[0][2]); 155 | pts2[2][1]=(m[1][0]*pts[2][0] + m[1][1]*pts[2][1] + m[1][2]); 156 | 157 | pts[0][0]=(m[0][0]*gblPts[0][0] + m[0][1]*gblPts[0][1] + m[0][2]); 158 | pts[0][1]=(m[1][0]*gblPts[0][0] + m[1][1]*gblPts[0][1] + m[1][2]); 159 | pts[1][0]=(m[0][0]*gblPts[1][0] + m[0][1]*gblPts[1][1] + m[0][2]); 160 | pts[1][1]=(m[1][0]*gblPts[1][0] + m[1][1]*gblPts[1][1] + m[1][2]); 161 | pts[2][0]=(m[0][0]*gblPts[2][0] + m[0][1]*gblPts[2][1] + m[0][2]); 162 | pts[2][1]=(m[1][0]*gblPts[2][0] + m[1][1]*gblPts[2][1] + m[1][2]); 163 | 164 | String s2="\n\n"+oB.index+":Second transformation (raw)\n"+pts[0][0]+" "+pts[0][1]+"\n"+pts[1][0]+" "+pts[1][1]+"\n"+pts[2][0]+" "+pts[2][1]; 165 | String s3="\n\n"+oB.index+":Second transformation (from first)\n"+pts2[0][0]+" "+pts2[0][1]+"\n"+pts2[1][0]+" "+pts2[1][1]+"\n"+pts2[2][0]+" "+pts2[2][1]; 166 | 167 | m = solveEQ(gblPts,pts2); 168 | 169 | pts[0][0]=(m[0][0]*gblPts[0][0] + m[0][1]*gblPts[0][1] + m[0][2]); 170 | pts[0][1]=(m[1][0]*gblPts[0][0] + m[1][1]*gblPts[0][1] + m[1][2]); 171 | pts[1][0]=(m[0][0]*gblPts[1][0] + m[0][1]*gblPts[1][1] + m[0][2]); 172 | pts[1][1]=(m[1][0]*gblPts[1][0] + m[1][1]*gblPts[1][1] + m[1][2]); 173 | pts[2][0]=(m[0][0]*gblPts[2][0] + m[0][1]*gblPts[2][1] + m[0][2]); 174 | pts[2][1]=(m[1][0]*gblPts[2][0] + m[1][1]*gblPts[2][1] + m[1][2]); 175 | 176 | //IJ.error(s+s2+s3+"\n\nBoth transformations\n"+pts[0][0]+" "+pts[0][1]+"\n"+pts[1][0]+" "+pts[1][1]+"\n"+pts[2][0]+" "+pts[2][1]); 177 | 178 | return new listElement(oA.index,m); 179 | } 180 | 181 | public void startWriting(){ 182 | try{ 183 | FileWriter fw= new FileWriter(savePath); 184 | fw.write("MultiStackReg Transformation File\n"); 185 | fw.write("File Version 1.0\n"); 186 | fw.write("1"); 187 | fw.close(); 188 | }catch(IOException e){} 189 | } 190 | 191 | public void keepWriting(Object ob){ 192 | if(!(ob instanceof listElement)) 193 | throw new ClassCastException(); 194 | double[][] m=((listElement)ob).matrix; 195 | try{ 196 | FileWriter fw= new FileWriter(savePath,true); 197 | fw.append("\nAFFINE\n"); 198 | fw.append("Source img: "+((listElement)ob).index+" Target img: "+((listElement)ob).index+"\n"); 199 | 200 | 201 | fw.append(gblPts[0][0]+"\t"+gblPts[0][1]+"\n"); 202 | fw.append(gblPts[1][0]+"\t"+gblPts[1][1]+"\n"); 203 | fw.append(gblPts[2][0]+"\t"+gblPts[2][1]+"\n"); 204 | fw.append("\n"); 205 | fw.append((m[0][0]*gblPts[0][0] + m[0][1]*gblPts[0][1] + m[0][2])+"\t"+(m[1][0]*gblPts[0][0] + m[1][1]*gblPts[0][1] + m[1][2])+"\n"); 206 | fw.append((m[0][0]*gblPts[1][0] + m[0][1]*gblPts[1][1] + m[0][2])+"\t"+(m[1][0]*gblPts[1][0] + m[1][1]*gblPts[1][1] + m[1][2])+"\n"); 207 | fw.append((m[0][0]*gblPts[2][0] + m[0][1]*gblPts[2][1] + m[0][2])+"\t"+(m[1][0]*gblPts[2][0] + m[1][1]*gblPts[2][1] + m[1][2])+"\n"); 208 | 209 | fw.close(); 210 | }catch(IOException e){} 211 | } 212 | 213 | public void processFile(String AorB){ 214 | String transform="AFFINE"; 215 | try{ 216 | FileReader fr; 217 | if (AorB == "A"){ 218 | fr=new FileReader(loadPathA); 219 | }else{ 220 | fr=new FileReader(loadPathB); 221 | } 222 | BufferedReader br = new BufferedReader(fr); 223 | String record; 224 | int separatorIndex; 225 | int sepInd2; 226 | String[] fields=new String[3]; 227 | 228 | record = br.readLine(); 229 | record = record.trim(); 230 | if (record.equals("Transformation")){ 231 | //the given file was not made in multistackreg, so let's treat it like a turboreg file 232 | record = br.readLine().trim(); 233 | if (record.equals("RIGID_BODY")) 234 | transform="RIGID_BODY"; 235 | else if (record.equals("AFFINE")) 236 | transform="AFFINE"; 237 | else{ 238 | IJ.error("We are only processing rigid body and affine \ntransformations at this time. Sorry."); 239 | System.exit(0); 240 | } 241 | 242 | for (int j=0;j<8;j++) 243 | record = br.readLine(); 244 | for (int i=0;i<3;i++){ 245 | record = br.readLine(); 246 | record = record.trim(); 247 | separatorIndex = record.indexOf('\t'); 248 | fields[0] = record.substring(0, separatorIndex); 249 | fields[1] = record.substring(separatorIndex); 250 | fields[0] = fields[0].trim(); 251 | fields[1] = fields[1].trim(); 252 | srcPtsA[i][0]=(new Double(fields[0])).doubleValue(); 253 | srcPtsA[i][1]=(new Double(fields[1])).doubleValue(); 254 | } 255 | if (gblPtsInit != true){ //we're initializing, copy the first set of points as reference 256 | for (int i=0;i<3;i++){ 257 | gblPts[i][0]=srcPtsA[i][0]; 258 | gblPts[i][1]=srcPtsA[i][1]; 259 | } 260 | if (transform=="RIGID_BODY"){ //we need the points to be acolinear 261 | gblPts[1][0] *= 0.5; 262 | gblPts[2][0] *= 1.5; 263 | gblPts[1][1] = gblPts[2][1]; 264 | } 265 | gblPtsInit=true; 266 | } 267 | record = br.readLine(); 268 | record = br.readLine(); 269 | for (int i=0;i<3;i++){ 270 | record = br.readLine(); 271 | record = record.trim(); 272 | separatorIndex = record.indexOf('\t'); 273 | fields[0] = record.substring(0, separatorIndex); 274 | fields[1] = record.substring(separatorIndex); 275 | fields[0] = fields[0].trim(); 276 | fields[1] = fields[1].trim(); 277 | tgtPtsA[i][0]=(new Double(fields[0])).doubleValue(); 278 | tgtPtsA[i][1]=(new Double(fields[1])).doubleValue(); 279 | } 280 | GenericDialog gd = new GenericDialog("Compress Matrices"); 281 | gd.addMessage("What images should this be applied to? (3, 5-10, etc)"); 282 | gd.addStringField("","",30); 283 | gd.showDialog(); 284 | if (gd.wasCanceled()) { 285 | return; 286 | } 287 | 288 | String index = gd.getNextString(); 289 | SortedSet set = stringToSet(index); 290 | 291 | listElement tmp; 292 | Iterator it = set.iterator(); 293 | while (it.hasNext()) { 294 | // Get element 295 | Object element = it.next(); 296 | 297 | double[][] mat;// = solveEQ(srcPtsA,tgtPtsA); 298 | 299 | if (transform.equals("RIGID_BODY")){ 300 | mat = solveRBEQ(srcPtsA,tgtPtsA); 301 | //TODO 302 | tgtPtsB[0][0]=500; 303 | tgtPtsB[1][0]=tgtPtsB[0][1]=250; 304 | tgtPtsB[2][0]=tgtPtsB[1][1]=tgtPtsB[2][1]=750; 305 | srcPtsB[0][0]=mat[0][1]*tgtPtsB[0][0]-mat[0][2]*tgtPtsB[0][1]-mat[0][0]; 306 | srcPtsB[0][1]=mat[1][2]*tgtPtsB[0][1]-mat[1][1]*tgtPtsB[0][0]-mat[1][0]; 307 | 308 | srcPtsB[1][0]=mat[0][1]*tgtPtsB[1][0]-mat[0][2]*tgtPtsB[1][1]-mat[0][0]; 309 | srcPtsB[1][1]=mat[1][2]*tgtPtsB[1][1]-mat[1][1]*tgtPtsB[1][0]-mat[1][0]; 310 | 311 | srcPtsB[2][0]=mat[0][1]*tgtPtsB[2][0]-mat[0][2]*tgtPtsB[2][1]-mat[0][0]; 312 | srcPtsB[2][1]=mat[1][2]*tgtPtsB[2][1]-mat[1][1]*tgtPtsB[2][0]-mat[1][0]; 313 | 314 | mat = solveEQ(srcPtsB,tgtPtsB); 315 | 316 | transform="AFFINE"; 317 | 318 | }else 319 | mat = solveEQ(srcPtsA,tgtPtsA); 320 | tmp = new listElement(((Integer)element).intValue(),mat); 321 | if (AorB == "A") 322 | listA.add(tmp); 323 | else 324 | listB.add(tmp); 325 | } 326 | }else if (record.equals("MultiStackReg Transformation File")){ 327 | //this is a multistackreg transformation file 328 | record = br.readLine(); 329 | record = br.readLine(); 330 | 331 | int twoStackAlignment=1; 332 | record = record.trim(); 333 | twoStackAlignment=(new Integer(record)).intValue(); 334 | double[][] globalTransform = { 335 | {1.0, 0.0, 0.0}, 336 | {0.0, 1.0, 0.0}, 337 | {0.0, 0.0, 1.0} 338 | }; 339 | double gx,gy; 340 | gx=gy=0; 341 | record = br.readLine(); 342 | int cont=1; 343 | int newIndex,srcIndex; 344 | listElement tmp,global,otmp; 345 | global = new listElement(0,globalTransform); 346 | if (record.equals("RIGID_BODY")) 347 | transform="RIGID_BODY"; 348 | else if (!record.equals("AFFINE")) { 349 | IJ.error("We are only processing rigid body and affine \ntransformations at this time. Sorry."); 350 | System.exit(0); 351 | } 352 | boolean forward = false; 353 | while (cont>0){ 354 | record = br.readLine(); 355 | if (record==null) break; 356 | separatorIndex = record.indexOf(" Target"); 357 | fields[0] = record.substring(separatorIndex+12).trim(); 358 | srcIndex=(new Integer(fields[0])).intValue(); 359 | separatorIndex = record.indexOf(" Target"); 360 | fields[0] = record.substring(0, separatorIndex); 361 | separatorIndex = fields[0].indexOf(":"); 362 | fields[1] = fields[0].substring(separatorIndex+1).trim(); 363 | newIndex = (new Integer(fields[1])).intValue(); 364 | if (!forward && twoStackAlignment==0 && 1srcIndex) { 365 | //at the halfway point in the stack, we need to reset the global matrix 366 | global.matrix[0][0] = global.matrix[1][1] = global.matrix[2][2] = 1.0; 367 | global.matrix[0][1] = global.matrix[0][2] = global.matrix[1][0] = 0.0; 368 | global.matrix[1][2] = global.matrix[2][0] = global.matrix[2][1] = 0.0; 369 | forward=true; 370 | } 371 | globalIndex=newIndex; 372 | for (int i=0;i<3;i++){ 373 | record = br.readLine(); 374 | record = record.trim(); 375 | separatorIndex = record.indexOf('\t'); 376 | fields[0] = record.substring(0, separatorIndex); 377 | fields[1] = record.substring(separatorIndex); 378 | fields[0] = fields[0].trim(); 379 | fields[1] = fields[1].trim(); 380 | srcPtsA[i][0]=(new Double(fields[0])).doubleValue(); 381 | srcPtsA[i][1]=(new Double(fields[1])).doubleValue(); 382 | } 383 | if (gblPtsInit != true){ //we're initializing, copy the first set of points as reference 384 | for (int i=0;i<3;i++){ 385 | gblPts[i][0]=srcPtsA[i][0]; 386 | gblPts[i][1]=srcPtsA[i][1]; 387 | } 388 | if (transform=="RIGID_BODY"){ //we need the points to be acolinear 389 | gblPts[1][0] *= 0.5; 390 | gblPts[2][0] *= 1.5; 391 | gblPts[1][1] = gblPts[2][1]; 392 | } 393 | gblPtsInit=true; 394 | } 395 | record = br.readLine(); 396 | for (int i=0;i<3;i++){ 397 | record = br.readLine(); 398 | record = record.trim(); 399 | separatorIndex = record.indexOf('\t'); 400 | fields[0] = record.substring(0, separatorIndex); 401 | fields[1] = record.substring(separatorIndex); 402 | fields[0] = fields[0].trim(); 403 | fields[1] = fields[1].trim(); 404 | tgtPtsA[i][0]=(new Double(fields[0])).doubleValue(); 405 | tgtPtsA[i][1]=(new Double(fields[1])).doubleValue(); 406 | } 407 | double[][] mat; 408 | if (transform.equals("RIGID_BODY")){ 409 | mat = solveRBEQ(srcPtsA,tgtPtsA); 410 | //TODO 411 | //IJ.error("SRC:\n"+srcPtsA[0][0]+" "+srcPtsA[0][1]+"\n"+srcPtsA[1][0]+" "+srcPtsA[1][1]+"\n"+srcPtsA[2][0]+" "+srcPtsA[2][1] 412 | // +"\n\nTGT:\n"+tgtPtsA[0][0]+" "+tgtPtsA[0][1]+"\n"+tgtPtsA[1][0]+" "+tgtPtsA[1][1]+"\n"+tgtPtsA[2][0]+" "+tgtPtsA[2][1]); 413 | 414 | srcPtsB[0][0]=500; 415 | srcPtsB[1][0]=srcPtsB[0][1]=250; 416 | srcPtsB[2][0]=srcPtsB[1][1]=srcPtsB[2][1]=750; 417 | 418 | //uncomment to test that the transformation can generate the points what spawned it 419 | /*for (int i=0;i<3;i++){ 420 | srcPtsB[i][0]=tgtPtsA[i][0]; 421 | srcPtsB[i][1]=tgtPtsA[i][1]; 422 | } */ 423 | 424 | tgtPtsB[0][0]=mat[0][1]*srcPtsB[0][0]+mat[0][2]*srcPtsB[0][1]+mat[0][0]; 425 | tgtPtsB[0][1]=mat[1][2]*srcPtsB[0][1]+mat[1][1]*srcPtsB[0][0]+mat[1][0]; 426 | 427 | tgtPtsB[1][0]=mat[0][1]*srcPtsB[1][0]+mat[0][2]*srcPtsB[1][1]+mat[0][0]; 428 | tgtPtsB[1][1]=mat[1][2]*srcPtsB[1][1]+mat[1][1]*srcPtsB[1][0]+mat[1][0]; 429 | 430 | tgtPtsB[2][0]=mat[0][1]*srcPtsB[2][0]+mat[0][2]*srcPtsB[2][1]+mat[0][0]; 431 | tgtPtsB[2][1]=mat[1][2]*srcPtsB[2][1]+mat[1][1]*srcPtsB[2][0]+mat[1][0]; 432 | 433 | //String s=mat[0][0]+" "+mat[0][1]+" "+mat[0][2]+"\n"+mat[1][0]+" "+mat[1][1]+" "+mat[1][2]; 434 | //IJ.error(s+"\n\n"+tgtPtsB[0][0]+" "+tgtPtsB[0][1]+"\n"+tgtPtsB[1][0]+" "+tgtPtsB[1][1]+"\n"+tgtPtsB[2][0]+" "+tgtPtsB[2][1]); 435 | 436 | mat = solveEQ(srcPtsB,tgtPtsB); 437 | 438 | transform="AFFINE"; 439 | 440 | }else 441 | mat = solveEQ(srcPtsA,tgtPtsA); 442 | otmp = new listElement(newIndex,mat,transform); 443 | if (twoStackAlignment == 0){ 444 | //String s=global.matrix[0][0]+" "+global.matrix[0][1]+" "+global.matrix[0][2]+"\n"+global.matrix[1][0]+" "+global.matrix[1][1]+" "+global.matrix[1][2]+"\n"+global.matrix[2][0]+" "+global.matrix[2][1]+" "+global.matrix[2][2]+"\n"; 445 | double[][] rescued = { 446 | {global.matrix[0][0], global.matrix[0][1], global.matrix[0][2]}, 447 | {global.matrix[1][0], global.matrix[1][1], global.matrix[1][2]}, 448 | {global.matrix[2][0], global.matrix[2][1], global.matrix[2][2]} 449 | }; 450 | for (int i = 0; (i < 3); i++) { 451 | for (int j = 0; (j < 3); j++) { 452 | global.matrix[i][j] = 0.0; 453 | for (int k = 0; (k < 3); k++) { 454 | global.matrix[i][j] += otmp.matrix[i][k] * rescued[k][j]; 455 | } 456 | } 457 | } 458 | double[][] rescued2 = { 459 | {global.matrix[0][0], global.matrix[0][1], global.matrix[0][2]}, 460 | {global.matrix[1][0], global.matrix[1][1], global.matrix[1][2]}, 461 | {global.matrix[2][0], global.matrix[2][1], global.matrix[2][2]} 462 | }; 463 | tmp=new listElement(newIndex,rescued2,transform); 464 | //IJ.error(newIndex+"\n"+tmp.matrix[0][0]+" "+tmp.matrix[0][1]+" "+tmp.matrix[0][2]+"\n"+tmp.matrix[1][0]+" "+tmp.matrix[1][1]+" "+tmp.matrix[1][2]+"\n"+tmp.matrix[2][0]+" "+tmp.matrix[2][1]+" "+tmp.matrix[2][2]+"\n"); 465 | }else{ 466 | tmp=otmp; 467 | } 468 | if (AorB == "A") 469 | listA.add(tmp); 470 | else 471 | listB.add(tmp); 472 | record = br.readLine(); 473 | record = br.readLine(); 474 | //IJ.error(record.trim()); 475 | if (record != null) 476 | if (record.equals("RIGID_BODY")) 477 | transform="RIGID_BODY"; 478 | else if (record.equals("AFFINE")) 479 | transform="AFFINE"; 480 | else{ 481 | IJ.error("We are only processing rigid body and affine \ntransformations at this time. Sorry."); 482 | System.exit(0); 483 | } 484 | } 485 | 486 | }else{ 487 | //dunno what this is. Crash horribly 488 | } 489 | }catch(FileNotFoundException e){ 490 | IJ.error("Could not find proper transformation matrix."); 491 | }catch (IOException e) { 492 | IJ.error("Error reading from file."); 493 | } 494 | 495 | } 496 | 497 | public SortedSet stringToSet(String in){ 498 | SortedSet set = new TreeSet(); 499 | in.trim(); 500 | int separatorIndex = in.indexOf(","); 501 | int sepInd2; 502 | String record; 503 | String[] fields=new String[3]; 504 | while (separatorIndex != -1){ 505 | record=in.substring(0, separatorIndex).trim(); 506 | in=in.substring(separatorIndex+1).trim(); 507 | separatorIndex = in.indexOf(","); 508 | sepInd2=record.indexOf("-"); 509 | if ( sepInd2 != -1){ 510 | fields[0]=record.substring(0, sepInd2).trim(); 511 | fields[1]=record.substring(sepInd2+1).trim(); 512 | for (int i=(new Integer(fields[0])).intValue();i<=(new Integer(fields[1])).intValue();i++){ 513 | set.add(new Integer(i)); 514 | } 515 | }else{ 516 | set.add(new Integer(record)); 517 | } 518 | } 519 | sepInd2=in.indexOf("-"); 520 | if ( sepInd2 != -1){ 521 | fields[0]=in.substring(0, sepInd2).trim(); 522 | fields[1]=in.substring(sepInd2+1).trim(); 523 | for (int i=(new Integer(fields[0])).intValue();i<=(new Integer(fields[1])).intValue();i++){ 524 | set.add(new Integer(i)); 525 | } 526 | }else{ 527 | set.add(new Integer(in)); 528 | } 529 | 530 | return set; 531 | } 532 | 533 | public double[][] solveRBEQ(double[][] srcPts, double[][] tgtPts){ 534 | final double angle = Math.atan2(srcPts[2][0] - srcPts[1][0], 535 | srcPts[2][1] - srcPts[1][1]) 536 | - Math.atan2(tgtPts[2][0] - tgtPts[1][0], 537 | tgtPts[2][1] - tgtPts[1][1]); 538 | final double c = Math.cos(angle); 539 | final double s = Math.sin(angle); 540 | double[][] matrix = new double[3][3]; 541 | matrix[0][0] = tgtPts[0][0] 542 | - c * srcPts[0][0] + s * srcPts[0][1]; 543 | matrix[0][1] = c; 544 | matrix[0][2] = -s; 545 | matrix[1][0] = tgtPts[0][1] 546 | - s * srcPts[0][0] - c * srcPts[0][1]; 547 | matrix[1][1] = s; 548 | matrix[1][2] = c; 549 | 550 | matrix[2][0]=matrix[2][1]=0; 551 | matrix[2][2]=1; 552 | 553 | //IJ.error(matrix[0][0]+" "+matrix[0][1]+" "+matrix[0][2]+"\n"+matrix[1][0]+" "+matrix[1][1]+" "+matrix[1][2]); 554 | return matrix; 555 | } 556 | 557 | public double[][] solveEQ(double[][] srcPts, double[][] tgtPts){ 558 | double[][] outPts = new double[3][3]; 559 | 560 | outPts[2][0]=outPts[2][1]=0; 561 | outPts[2][2]=1; 562 | 563 | 564 | double[][] tmp = new double[2][3]; 565 | 566 | tmp[0][0] = srcPts[1][1]-(srcPts[0][1]*srcPts[1][0]/(srcPts[0][0]+0.00000001)); 567 | tmp[0][1] = 1-(1*srcPts[1][0]/(srcPts[0][0]+0.00000001)); 568 | tmp[0][2] = tgtPts[1][1]-(tgtPts[0][1]*srcPts[1][0]/(srcPts[0][0]+0.00000001)); 569 | 570 | tmp[1][0] = srcPts[2][1]-(srcPts[0][1]*srcPts[2][0]/(srcPts[0][0]+0.00000001)); 571 | tmp[1][1] = 1-(1*srcPts[2][0]/(srcPts[0][0]+0.00000001)); 572 | tmp[1][2] = tgtPts[2][1]-(tgtPts[0][1]*srcPts[2][0]/(srcPts[0][0]+0.00000001)); 573 | 574 | tmp[1][1] = tmp[1][1]-(tmp[0][1]*tmp[1][0]/(tmp[0][0]+0.00000001)); 575 | tmp[1][2] = tmp[1][2]-(tmp[0][2]*tmp[1][0]/(tmp[0][0]+0.00000001)); 576 | 577 | 578 | double C = tmp[1][2]/(tmp[1][1]+0.00000001); 579 | double B = (tmp[0][2]-C*tmp[0][1])/(tmp[0][0]+0.00000001); 580 | double A = (tgtPts[0][1]-C-B*srcPts[0][1])/(srcPts[0][0]+0.00000001); 581 | 582 | outPts[1][0] = A; 583 | outPts[1][1] = B; 584 | outPts[1][2] = C; 585 | 586 | 587 | 588 | tmp[0][0] = srcPts[1][1]-(srcPts[0][1]*srcPts[1][0]/(srcPts[0][0]+0.00000001)); 589 | tmp[0][1] = 1-(1*srcPts[1][0]/(srcPts[0][0]+0.00000001)); 590 | tmp[0][2] = tgtPts[1][0]-(tgtPts[0][0]*srcPts[1][0]/(srcPts[0][0]+0.00000001)); 591 | 592 | tmp[1][0] = srcPts[2][1]-(srcPts[0][1]*srcPts[2][0]/(srcPts[0][0]+0.00000001)); 593 | tmp[1][1] = 1-(1*srcPts[2][0]/(srcPts[0][0]+0.00000001)); 594 | tmp[1][2] = tgtPts[2][0]-(tgtPts[0][0]*srcPts[2][0]/(srcPts[0][0]+0.00000001)); 595 | 596 | tmp[1][1] = tmp[1][1]-(tmp[0][1]*tmp[1][0]/(tmp[0][0]+0.00000001)); 597 | tmp[1][2] = tmp[1][2]-(tmp[0][2]*tmp[1][0]/(tmp[0][0]+0.00000001)); 598 | 599 | 600 | C = tmp[1][2]/(tmp[1][1]+0.00000001); 601 | B = (tmp[0][2]-C*tmp[0][1])/(tmp[0][0]+0.00000001); 602 | A = (tgtPts[0][0]-C-B*srcPts[0][1])/(srcPts[0][0]+0.00000001); 603 | 604 | outPts[0][0] = A; 605 | outPts[0][1] = B; 606 | outPts[0][2] = C; 607 | 608 | return outPts; 609 | } 610 | 611 | public class listElement implements Comparable{ 612 | 613 | public int index; 614 | private double[][] matrix; 615 | private String transform; 616 | 617 | public listElement(int ind, double[][] mat){ 618 | index=ind; 619 | matrix=mat; 620 | transform="AFFINE"; 621 | } 622 | 623 | public listElement(int ind, double[][] mat, String trans){ 624 | index=ind; 625 | matrix=mat; 626 | transform=new String(trans); 627 | } 628 | 629 | public int compareTo(Object o2){ 630 | if(!(o2 instanceof listElement)) 631 | throw new ClassCastException(); 632 | 633 | int result = ((listElement)o2).index;//compare(new Integer(index),new Integer(((listElement)o2).index)); 634 | 635 | return index - result; 636 | } 637 | 638 | 639 | } 640 | 641 | 642 | 643 | 644 | } 645 | 646 | 647 | 648 | 649 | 650 | 651 | -------------------------------------------------------------------------------- /src/main/java/de/embl/cmci/registration/MultiStackRegCredits.java: -------------------------------------------------------------------------------- 1 | package de.embl.cmci.registration; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Button; 5 | import java.awt.Dialog; 6 | import java.awt.FlowLayout; 7 | import java.awt.Frame; 8 | import java.awt.Insets; 9 | import java.awt.Label; 10 | import java.awt.Panel; 11 | import java.awt.TextArea; 12 | import java.awt.event.ActionEvent; 13 | import java.awt.event.ActionListener; 14 | 15 | /*==================================================================== 16 | | stackRegCredits 17 | \===================================================================*/ 18 | public class MultiStackRegCredits extends Dialog { /* begin class multiStackRegCredits */ 19 | 20 | /*.................................................................... 21 | Public methods 22 | ....................................................................*/ 23 | 24 | /** 25 | * 26 | */ 27 | private static final long serialVersionUID = 1L; 28 | 29 | /********************************************************************/ 30 | public Insets getInsets ( 31 | ) { 32 | return(new Insets(0, 20, 20, 20)); 33 | } /* end getInsets */ 34 | 35 | /********************************************************************/ 36 | public MultiStackRegCredits ( final Frame parentWindow) { 37 | super(parentWindow, "StackReg", true); 38 | setLayout(new BorderLayout(0, 20)); 39 | final Label separation = new Label(""); 40 | final Panel buttonPanel = new Panel(); 41 | buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER)); 42 | final Button doneButton = new Button("Done"); 43 | doneButton.addActionListener( 44 | new ActionListener ( 45 | ) { 46 | public void actionPerformed ( 47 | final ActionEvent ae 48 | ) { 49 | if (ae.getActionCommand().equals("Done")) { 50 | dispose(); 51 | } 52 | } 53 | } 54 | ); 55 | buttonPanel.add(doneButton); 56 | final TextArea text = new TextArea(25, 56); 57 | text.setEditable(false); 58 | text.append("\n"); 59 | text.append("Welcome to MultiStackReg v1.4!\n"); 60 | text.append("\n"); 61 | text.append("This plugin has three modes of use:\n"); 62 | text.append("1) Align a single stack of images\n"); 63 | text.append("2) Align a stack to another stack\n"); 64 | text.append("3) Load a previously created transformation file\n"); 65 | 66 | text.append("\n"); 67 | text.append("\n"); 68 | text.append("To align a single stack:\n"); 69 | text.append("\n"); 70 | text.append("Choose the image to be aligned in the Stack 1 dropdown box.\n"); 71 | text.append("Leave 'Align' in the Action 1 box.\n"); 72 | text.append("\n"); 73 | text.append("\n"); 74 | text.append("To align two stacks:\n"); 75 | text.append("\n"); 76 | text.append("Place the reference stack in Stack 1's box, and the stack to be\n"); 77 | text.append("aligned in Stack 2's box. Select 'Use as Reference' as Action 1\n"); 78 | text.append("and 'Align to First Stack' as Action 2.\n"); 79 | text.append("\n"); 80 | text.append("\n"); 81 | text.append("To load a transformation file:\n"); 82 | text.append("\n"); 83 | text.append("Place the stack to be aligned in either stack box, choose 'Load \n"); 84 | text.append("Transformation File' in its Action dropdown box. The File field\n"); 85 | text.append("can be used as a shortcut to avoid having to select the matrix\n"); 86 | text.append("manually.\n"); 87 | text.append("\n"); 88 | text.append("\n"); 89 | text.append("\n"); 90 | text.append("Credits:\n"); 91 | text.append("\n"); 92 | text.append("\n"); 93 | text.append(" This work is based on the following paper:\n"); 94 | text.append("\n"); 95 | text.append(" P. Th" + (char)233 + "venaz, U.E. Ruttimann, M. Unser\n"); 96 | text.append(" A Pyramid Approach to Subpixel Registration Based on Intensity\n"); 97 | text.append(" IEEE Transactions on Image Processing\n"); 98 | text.append(" vol. 7, no. 1, pp. 27-41, January 1998.\n"); 99 | text.append("\n"); 100 | text.append(" This paper is available on-line at\n"); 101 | text.append(" http://bigwww.epfl.ch/publications/thevenaz9801.html\n"); 102 | text.append("\n"); 103 | text.append(" Other relevant on-line publications are available at\n"); 104 | text.append(" http://bigwww.epfl.ch/publications/\n"); 105 | text.append("\n"); 106 | text.append(" Additional help available at\n"); 107 | text.append(" http://bigwww.epfl.ch/thevenaz/stackreg/\n"); 108 | text.append("\n"); 109 | text.append(" Ancillary TurboReg_ plugin available at\n"); 110 | text.append(" http://bigwww.epfl.ch/thevenaz/turboreg/\n"); 111 | text.append("\n"); 112 | text.append(" You'll be free to use this software for research purposes, but\n"); 113 | text.append(" you should not redistribute it without our consent. In addition,\n"); 114 | text.append(" we expect you to include a citation or acknowledgment whenever\n"); 115 | text.append(" you present or publish results that are based on it.\n"); 116 | text.append("\n\n"); 117 | text.append("A few changes (loadTransform, appendTransform, multi stack support)\n"); 118 | text.append("to support load/save functionality and multiple stacks were\n"); 119 | text.append("added by Brad Busse ( bbusse@stanford.edu ) and released into\n "); 120 | text.append("the public domain, so go by their ^^ guidelines for distribution, etc.\n"); 121 | add("North", separation); 122 | add("Center", text); 123 | add("South", buttonPanel); 124 | pack(); 125 | } /* end stackRegCredits */ 126 | 127 | } /* end class stackRegCredits */ 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/de/embl/cmci/registration/MultiStackReg_.java: -------------------------------------------------------------------------------- 1 | package de.embl.cmci.registration; 2 | 3 | /*==================================================================== 4 | | Version: August 28, 2007 5 | \===================================================================*/ 6 | 7 | /* Usage: 8 | * MultiStackReg can align a stack to itself, as in regular StackReg, or one stack to another. 9 | * 10 | * To align one stack to another, place the reference stack in the first slot, and the stack to be 11 | * aligned in the second. MultiStackReg will align each slice of the second stack to the 12 | * corresponding slice in the first stack. Note that both stacks must be the same length. 13 | * 14 | * To align a single stack, place it in the first slot and nothing in the second. Each slice will 15 | * be aligned as in normal stackreg. 16 | * 17 | * The save checkbox can be used to save the transformation matrix alignment results in, 18 | * and the load dropdown will apply a previously saved matrix to the selected stack. 19 | */ 20 | 21 | /*==================================================================== 22 | | EPFL/STI/IOA/LIB 23 | | Philippe Thevenaz 24 | | Bldg. BM-Ecublens 4.137 25 | | Station 17 26 | | CH-1015 Lausanne VD 27 | | Switzerland 28 | | 29 | | phone (CET): +41(21)693.51.61 30 | | fax: +41(21)693.37.01 31 | | RFC-822: philippe.thevenaz@epfl.ch 32 | | X-400: /C=ch/A=400net/P=switch/O=epfl/S=thevenaz/G=philippe/ 33 | | URL: http://bigwww.epfl.ch/ 34 | \===================================================================*/ 35 | 36 | /*==================================================================== 37 | | This work is based on the following paper: 38 | | 39 | | P. Thevenaz, U.E. Ruttimann, M. Unser 40 | | A Pyramid Approach to Subpixel Registration Based on Intensity 41 | | IEEE Transactions on Image Processing 42 | | vol. 7, no. 1, pp. 27-41, January 1998. 43 | | 44 | | This paper is available on-line at 45 | | http://bigwww.epfl.ch/publications/thevenaz9801.html 46 | | 47 | | Other relevant on-line publications are available at 48 | | http://bigwww.epfl.ch/publications/ 49 | \===================================================================*/ 50 | 51 | /*==================================================================== 52 | | Additional help available at http://bigwww.epfl.ch/thevenaz/stackreg/ 53 | | Ancillary TurboReg_ plugin available at: http://bigwww.epfl.ch/thevenaz/turboreg/ 54 | | 55 | | You'll be free to use this software for research purposes, but you 56 | | should not redistribute it without our consent. In addition, we expect 57 | | you to include a citation or acknowledgment whenever you present or 58 | | publish results that are based on it. 59 | \===================================================================*/ 60 | 61 | /* A few small changes (loadTransform, appendTransform, multi stack support) to 62 | * support load/save functionality and multiple stacks were added by Brad Busse 63 | * ( bbusse@stanford.edu ) and released into the public domain, so go by 64 | * their ^^ guidelines for distribution, etc. 65 | */ 66 | /* Modified for scripting and minor debuggings, over the release by Brad Busse. 67 | * Kota Miura 68 | */ 69 | 70 | // ImageJ 71 | import ij.IJ; 72 | import ij.ImagePlus; 73 | import ij.WindowManager; 74 | import ij.gui.GUI; 75 | import ij.gui.GenericDialog; 76 | import ij.io.FileSaver; 77 | import ij.plugin.PlugIn; 78 | import ij.process.Blitter; 79 | import ij.process.ByteProcessor; 80 | import ij.process.ColorProcessor; 81 | import ij.process.FloatProcessor; 82 | import ij.process.ImageConverter; 83 | import ij.process.ShortProcessor; 84 | import ij.plugin.ChannelSplitter; 85 | 86 | import java.awt.FileDialog; 87 | import java.awt.Frame; 88 | import java.awt.image.IndexColorModel; 89 | import java.nio.file.Files; 90 | import java.io.File; 91 | import java.io.FileReader; 92 | import java.io.FileNotFoundException; 93 | import java.io.FileWriter; 94 | import java.io.BufferedReader; 95 | import java.io.IOException; 96 | import java.lang.reflect.InvocationTargetException; 97 | //import java.lang.reflect.NoSuchMethodException; 98 | import java.lang.reflect.Method; 99 | import java.util.Stack; 100 | import java.util.UUID; 101 | import java.lang.SecurityException; 102 | 103 | /*==================================================================== 104 | | StackReg_ 105 | \===================================================================*/ 106 | 107 | /********************************************************************/ 108 | public class MultiStackReg_ 109 | implements 110 | PlugIn 111 | 112 | { /* begin class StackReg_ */ 113 | 114 | /*.................................................................... 115 | Private global variables 116 | ....................................................................*/ 117 | private static final double TINY = (double)Float.intBitsToFloat((int)0x33FFFFFF); 118 | private String loadPathAndFilename = ""; 119 | private String savePath = ""; 120 | private String saveFile; 121 | private String loadPath; 122 | private String loadFile; 123 | private int transformNumber = 0; 124 | private int tSlice; 125 | private int transformation; 126 | private boolean saveTransform; 127 | private boolean twoStackAlign; 128 | private boolean viewManual; 129 | private boolean loadSingleMatrix; 130 | private boolean fairlyWarned; 131 | private ImagePlus srcImg; 132 | private ImagePlus tgtImg; 133 | private String srcAction; 134 | private String tgtAction; 135 | 136 | /*.................................................................... 137 | Public global variables 138 | ....................................................................*/ 139 | 140 | public static final int TRANSLATION = 0; 141 | public static final int RIGID_BODY = 1; 142 | public static final int SCALED_ROTATION = 2; 143 | public static final int AFFINE = 3; 144 | 145 | /* path keepers for deleteing them afterwards */ 146 | 147 | String CsourcePathAndFileNameR = ""; 148 | String CsourcePathAndFileNameG = ""; 149 | String CsourcePathAndFileNameB = ""; 150 | String CsourcePathAndFileName = ""; 151 | String CtargetPathAndFileName = ""; 152 | 153 | /*.................................................................... 154 | Public methods 155 | ....................................................................*/ 156 | 157 | /******************************************************************* 158 | * {@link ij.ImagePlus} 159 | * */ 160 | public void run ( final String arg) { 161 | //loadPathAndFilename=""; 162 | //savePath=""; 163 | //transformNumber=0; 164 | Runtime.getRuntime().gc(); 165 | 166 | ImagePlus imp = WindowManager.getCurrentImage(); 167 | if (imp == null) { 168 | IJ.error("No image available"); 169 | return; 170 | } 171 | 172 | final ImagePlus[] admissibleImageList = createAdmissibleImageList(); 173 | final String[] sourceNames = new String[1+admissibleImageList.length]; 174 | sourceNames[0]="None"; 175 | fairlyWarned=false; 176 | for (int k = 0; (k < admissibleImageList.length); k++) { 177 | sourceNames[k+1]=admissibleImageList[k].getTitle(); 178 | } 179 | 180 | final String[] targetNames = new String[1+admissibleImageList.length]; 181 | targetNames[0]="None"; 182 | for (int k = 0; (k < admissibleImageList.length); k++) { 183 | targetNames[k+1]=admissibleImageList[k].getTitle(); 184 | } 185 | 186 | //if there are no grayscale images, just quit 187 | if (admissibleImageList.length == 0) return; 188 | 189 | final String[] sourceAction = { 190 | "Ignore", 191 | "Align", 192 | "Use as Reference", 193 | "Load Transformation File" 194 | }; 195 | 196 | final String[] targetAction = { 197 | "Ignore", 198 | "Align to First Stack", 199 | "Load Transformation File" 200 | }; 201 | 202 | final String[] transformationItem = { 203 | "Translation", 204 | "Rigid Body", 205 | "Scaled Rotation", 206 | "Affine" 207 | //"Load Transformation File" 208 | }; 209 | 210 | GenericDialog gd = new GenericDialog("MultiStackReg"); 211 | gd.addChoice("Stack_1:", sourceNames, admissibleImageList[0].getTitle()); 212 | gd.addChoice("Action_1:",sourceAction,"Align"); 213 | gd.addStringField("File_1 (optional):","",20); 214 | gd.addMessage(""); 215 | gd.addChoice("Stack_2:", targetNames, "None"); 216 | gd.addChoice("Action_2:",targetAction,"Ignore"); 217 | gd.addStringField("File_2 (optional):","",20); 218 | gd.addMessage(""); 219 | gd.addChoice("Transformation:", transformationItem, "Rigid Body"); 220 | //gd.addCheckbox("Align Second Stack To First", false); 221 | gd.addCheckbox("Save Transformation File", false); 222 | gd.addCheckbox("View Manual Instead", false); 223 | 224 | gd.showDialog(); 225 | if (gd.wasCanceled()) { 226 | return; 227 | } 228 | 229 | int tmpIndex=gd.getNextChoiceIndex(); 230 | srcImg=null; 231 | if (tmpIndex > 0){ 232 | srcImg = admissibleImageList[tmpIndex-1]; 233 | } 234 | srcAction=sourceAction[gd.getNextChoiceIndex()]; 235 | String srcFile=gd.getNextString(); 236 | 237 | tmpIndex=gd.getNextChoiceIndex(); 238 | tgtImg=null; 239 | if (tmpIndex > 0){ 240 | tgtImg = admissibleImageList[tmpIndex-1]; 241 | } 242 | tgtAction=targetAction[gd.getNextChoiceIndex()]; 243 | String tgtFile=gd.getNextString(); 244 | 245 | imp=srcImg; 246 | transformation = gd.getNextChoiceIndex(); 247 | //twoStackAlign = gd.getNextBoolean(); 248 | saveTransform = gd.getNextBoolean(); 249 | viewManual=gd.getNextBoolean(); 250 | 251 | //We've read all the values in. Let's try to figure out what the user wants us to do. 252 | twoStackAlign=false; 253 | loadPath=""; 254 | loadFile="None"; 255 | 256 | //@TODO show help with a help button, rather than a radio button. 257 | if (viewManual){ //they just want to read the manual. Do so and quit. 258 | final MultiStackRegCredits dialog = new MultiStackRegCredits(IJ.getInstance()); 259 | GUI.center(dialog); 260 | dialog.setVisible(true); 261 | return; 262 | } 263 | 264 | if ((srcImg==null || srcAction=="Ignore") && (tgtImg==null || tgtAction=="Ignore")){ 265 | //the user has deselected both stacks. Ask what's up and quit. 266 | IJ.error("Both stacks appear to be ignored.\nI'm... just gonna quit, then."); 267 | return; 268 | } 269 | core(srcFile, tgtFile); 270 | } 271 | 272 | /** 273 | * srcFile: full path to a text file, 274 | * (1) If "Align" or "As Reference" is selected and save transformation is checked, 275 | * transformation will be logged. 276 | * (2) If "As Reference" is selected and srcFile=="", then a dummy text file is created. 277 | * (3) If "Load Transformation file" is selected, this file will be the reference. 278 | * If srfFile =="", then a dialog pops up. 279 | * tgtFile: full path to a textfile, 280 | * (1) If "Align to first stack" is selected, then srcFile or dummy file is referred for transformation. 281 | * (2) If "Load Transformation File" is selected, then tgtFile will be the reference for transformation. 282 | * If tgtFile=="", then diaplog pops up. 283 | * 284 | * 285 | */ 286 | public void core(String srcFile, String tgtFile){ 287 | 288 | if (srcImg != null && (srcAction=="Ignore" || srcAction=="Use as Reference") && 289 | tgtImg != null && tgtAction=="Load Transformation File"){ 290 | //we're not doing anything with our source image, but the user wants to load a file on the second. *shrug* Do it, I guess. 291 | if (!tgtFile.equals("")) 292 | loadFile=tgtFile; 293 | processDirectives(tgtImg,true); 294 | return; 295 | } 296 | 297 | if (srcImg != null && srcAction=="Load Transformation File" && 298 | (tgtAction=="Load Transformation File" || 299 | tgtImg == null || tgtAction=="Ignore")){ 300 | //load a file on our first matrix, and do anything but align the second 301 | if (!srcFile.equals("")) 302 | loadFile=srcFile; 303 | processDirectives(srcImg,true); 304 | loadPath=""; 305 | loadFile="None"; 306 | if (tgtImg != null && tgtAction=="Load Transformation File"){ 307 | if (!tgtFile.equals("")) 308 | loadFile=tgtFile; 309 | processDirectives(tgtImg,true); 310 | } 311 | return; 312 | } 313 | 314 | //only ask to save the transformation if at least one stack is marked for alignment 315 | //(This is why I've split everything up so much) 316 | if (saveTransform){ 317 | if (srcAction=="Align" && srcFile.equals("")){ 318 | final Frame f = new Frame(); 319 | final FileDialog fd = new FileDialog( f, "Save transformations at", FileDialog.SAVE); 320 | String filename = "TransformationMatrices.txt"; 321 | fd.setFile(filename); 322 | fd.setVisible(true); 323 | if (fd.getFile() == null){ 324 | IJ.error("Action cancelled"); 325 | return; 326 | } 327 | srcFile=fd.getDirectory()+fd.getFile(); 328 | } 329 | if (tgtAction=="Align to First Stack" && tgtFile.equals("")){ 330 | final Frame f = new Frame(); 331 | final FileDialog fd = new FileDialog( f, "Save transformations at", FileDialog.SAVE); 332 | String filename = "TransformationMatrices.txt"; 333 | fd.setFile(filename); 334 | fd.setVisible(true); 335 | if (fd.getFile() == null){ 336 | IJ.error("Action cancelled"); 337 | return; 338 | } 339 | tgtFile=fd.getDirectory()+fd.getFile(); 340 | } 341 | //savePath=fd.getDirectory(); 342 | //saveFile=fd.getFile(); 343 | 344 | } 345 | 346 | savePath=""; 347 | 348 | if (srcImg != null && srcAction=="Align"){ 349 | //classic stackreg mode 350 | if (tgtImg != null && tgtAction=="Load Transformation File"){ 351 | //we want to immediately apply the first alignment to the second stack 352 | if (!saveTransform){ 353 | //we aren't saving the alignment, so make a temp file 354 | saveTransform=true; 355 | savePath=""; 356 | saveFile="deleteme.txt"; 357 | } 358 | } 359 | //align the first stack 360 | saveFile=srcFile; 361 | processDirectives(srcImg,false); 362 | 363 | if (tgtImg != null && tgtAction=="Load Transformation File"){ 364 | //apply it to the second stack 365 | loadPath=savePath; 366 | loadFile=saveFile; 367 | processDirectives(tgtImg,true); 368 | } 369 | 370 | if (tgtImg != null && tgtAction=="Align to First Stack"){ 371 | //two-stack alignment mode 372 | twoStackAlign=true; 373 | saveFile=tgtFile; 374 | processDirectives(srcImg,false); 375 | } 376 | return; 377 | } 378 | 379 | if (srcImg != null && (srcAction=="Ignore" || srcAction=="Use as Reference") && 380 | tgtImg != null && tgtAction=="Align to First Stack"){ 381 | //two-stack alignment mode 382 | twoStackAlign=true; 383 | saveFile=tgtFile; 384 | processDirectives(srcImg,false); 385 | } 386 | 387 | if (srcImg != null && srcAction=="Load Transformation File"){ 388 | //load a file on our first matrix 389 | if (!srcFile.equals("")) 390 | loadFile=srcFile; 391 | processDirectives(srcImg,true); 392 | if (tgtImg != null && tgtAction=="Align to First Stack"){ 393 | //two-stack alignment mode 394 | twoStackAlign=true; 395 | saveFile=tgtFile; 396 | processDirectives(srcImg,false); 397 | } 398 | return; 399 | } 400 | 401 | 402 | }//end run 403 | 404 | 405 | 406 | /**.................................................................... 407 | * 408 | ....................................................................*/ 409 | public int processDirectives(ImagePlus imp, boolean loadBool){ 410 | if (twoStackAlign && !loadBool){ //we want to do two-stack alignment, check that the stacks are compatible 411 | if (srcImg.getImageStackSize() != tgtImg.getImageStackSize()){ 412 | IJ.error("Stack sizes must match."); 413 | return 0; 414 | } 415 | if (srcImg.getHeight() != tgtImg.getHeight() || srcImg.getWidth() != tgtImg.getWidth()){ 416 | IJ.error("Stack dimensions must match."); 417 | return 0; 418 | } 419 | if (srcImg.getType() != tgtImg.getType()){ 420 | IJ.error("Stack image types must match."); 421 | return 0; 422 | } 423 | } 424 | 425 | if (loadBool) { 426 | if (loadFile=="None"){ 427 | final Frame t = new Frame(); 428 | final FileDialog fl = new FileDialog( t, "Load transformation file", FileDialog.LOAD); 429 | fl.setVisible(true); 430 | if (fl.getFile() == null){ 431 | IJ.error("Action cancelled"); 432 | return 0; 433 | } 434 | loadPathAndFilename = fl.getDirectory()+fl.getFile(); 435 | }else{ 436 | loadPathAndFilename = loadPath+loadFile; 437 | } 438 | int tgt = loadTransform(0, null, null); 439 | transformation = loadTransform(1, null, null); 440 | imp.setSlice(tgt); 441 | } 442 | final int width = imp.getWidth(); 443 | final int height = imp.getHeight(); 444 | final int targetSlice = imp.getCurrentSlice(); 445 | tSlice=targetSlice; 446 | double[][] globalTransform = { 447 | {1.0, 0.0, 0.0}, 448 | {0.0, 1.0, 0.0}, 449 | {0.0, 0.0, 1.0} 450 | }; 451 | double[][] anchorPoints = null; 452 | switch (transformation) { 453 | case TRANSLATION: { //AFFINE: 454 | anchorPoints = new double[1][3]; 455 | anchorPoints[0][0] = (double)(width / 2); 456 | anchorPoints[0][1] = (double)(height / 2); 457 | anchorPoints[0][2] = 1.0; 458 | break; 459 | } 460 | case RIGID_BODY: { //three points 461 | anchorPoints = new double[3][3]; 462 | anchorPoints[0][0] = (double)(width / 2); 463 | anchorPoints[0][1] = (double)(height / 2); 464 | anchorPoints[0][2] = 1.0; 465 | anchorPoints[1][0] = (double)(width / 2); 466 | anchorPoints[1][1] = (double)(height / 4); 467 | anchorPoints[1][2] = 1.0; 468 | anchorPoints[2][0] = (double)(width / 2); 469 | anchorPoints[2][1] = (double)((3 * height) / 4); 470 | anchorPoints[2][2] = 1.0; 471 | break; 472 | } 473 | case SCALED_ROTATION: { //two points 474 | anchorPoints = new double[2][3]; 475 | anchorPoints[0][0] = (double)(width / 4); 476 | anchorPoints[0][1] = (double)(height / 2); 477 | anchorPoints[0][2] = 1.0; 478 | anchorPoints[1][0] = (double)((3 * width) / 4); 479 | anchorPoints[1][1] = (double)(height / 2); 480 | anchorPoints[1][2] = 1.0; 481 | break; 482 | } 483 | case AFFINE: {//TRANSLATION: { 484 | anchorPoints = new double[3][3]; 485 | anchorPoints[0][0] = (double)(width / 2); 486 | anchorPoints[0][1] = (double)(height / 4); 487 | anchorPoints[0][2] = 1.0; 488 | anchorPoints[1][0] = (double)(width / 4); 489 | anchorPoints[1][1] = (double)((3 * height) / 4); 490 | anchorPoints[1][2] = 1.0; 491 | anchorPoints[2][0] = (double)((3 * width) / 4); 492 | anchorPoints[2][1] = (double)((3 * height) / 4); 493 | anchorPoints[2][2] = 1.0; 494 | break; 495 | } 496 | default: { 497 | IJ.error("Unexpected transformation"); 498 | return 0; 499 | } 500 | } 501 | ImagePlus source = null; 502 | ImagePlus target = null; 503 | double[] colorWeights = null; 504 | switch (imp.getType()) { 505 | case ImagePlus.COLOR_256: 506 | case ImagePlus.COLOR_RGB: { 507 | colorWeights = getColorWeightsFromPrincipalComponents(imp); 508 | imp.setSlice(targetSlice); 509 | target = getGray32("StackRegTarget", imp, colorWeights); 510 | break; 511 | } 512 | case ImagePlus.GRAY8: { 513 | target = new ImagePlus("StackRegTarget", 514 | new ByteProcessor(width, height, new byte[width * height], 515 | imp.getProcessor().getColorModel())); 516 | target.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 517 | break; 518 | } 519 | case ImagePlus.GRAY16: { 520 | target = new ImagePlus("StackRegTarget", 521 | new ShortProcessor(width, height, new short[width * height], 522 | imp.getProcessor().getColorModel())); 523 | target.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 524 | break; 525 | } 526 | case ImagePlus.GRAY32: { 527 | target = new ImagePlus("StackRegTarget", 528 | new FloatProcessor(width, height, new float[width * height], 529 | imp.getProcessor().getColorModel())); 530 | target.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 531 | break; 532 | } 533 | default: { 534 | IJ.error("Unexpected image type"); 535 | return 0; 536 | } 537 | } 538 | //we've specified a file to load. Load it, process it 539 | String path=""; 540 | if (loadPathAndFilename!=""){ 541 | }else if (saveTransform){ 542 | path=savePath+saveFile; 543 | try{ 544 | FileWriter fw= new FileWriter(path); 545 | fw.write("MultiStackReg Transformation File\n"); 546 | fw.write("File Version 1.0\n"); 547 | if (twoStackAlign) 548 | fw.write("1\n"); 549 | else 550 | fw.write("0\n"); 551 | fw.close(); 552 | }catch(IOException e){} 553 | } 554 | if (twoStackAlign){ 555 | target = getSlice(imp,targetSlice); 556 | if (!loadBool) 557 | source = registerSlice(source, target, tgtImg, width, height, 558 | transformation, globalTransform, anchorPoints, colorWeights, targetSlice); 559 | else 560 | source = registerSlice(source, target, imp, width, height, 561 | transformation, globalTransform, anchorPoints, colorWeights, targetSlice); 562 | if (source == null) return 2; 563 | } 564 | if (loadSingleMatrix){ 565 | source = registerSlice(source, target, imp, width, height, 566 | transformation, globalTransform, anchorPoints, colorWeights, targetSlice); 567 | if (source == null) return 2; 568 | } 569 | for (int s = targetSlice - 1; (0 < s); s--) { 570 | if (twoStackAlign){ 571 | globalTransform[0][0] = globalTransform[1][1] = globalTransform[2][2] = 1.0; 572 | globalTransform[0][1] = globalTransform[0][2] = globalTransform[1][0] = 0.0; 573 | globalTransform[1][2] = globalTransform[2][0] = globalTransform[2][1] = 0.0; 574 | target = getSlice(imp,s); 575 | if (!loadBool) 576 | source = registerSlice(source, target, tgtImg, width, height, 577 | transformation, globalTransform, anchorPoints, colorWeights, s); 578 | else 579 | source = registerSlice(source, target, imp, width, height, 580 | transformation, globalTransform, anchorPoints, colorWeights, s); 581 | if (source == null) return 2; 582 | }else{ 583 | if (loadSingleMatrix){ //with one transformation only, we need to reset the global transform each time 584 | globalTransform[0][0] = globalTransform[1][1] = globalTransform[2][2] = 1.0; 585 | globalTransform[0][1] = globalTransform[0][2] = globalTransform[1][0] = 0.0; 586 | globalTransform[1][2] = globalTransform[2][0] = globalTransform[2][1] = 0.0; 587 | } 588 | source = registerSlice(source, target, imp, width, height, 589 | transformation, globalTransform, anchorPoints, colorWeights, s); 590 | if (source == null) return 2; 591 | } 592 | } 593 | if ((1 < targetSlice) && (targetSlice < imp.getStackSize())) { 594 | globalTransform[0][0] = 1.0; 595 | globalTransform[0][1] = 0.0; 596 | globalTransform[0][2] = 0.0; 597 | globalTransform[1][0] = 0.0; 598 | globalTransform[1][1] = 1.0; 599 | globalTransform[1][2] = 0.0; 600 | globalTransform[2][0] = 0.0; 601 | globalTransform[2][1] = 0.0; 602 | globalTransform[2][2] = 1.0; 603 | imp.setSlice(targetSlice); 604 | switch (imp.getType()) { 605 | case ImagePlus.COLOR_256: 606 | case ImagePlus.COLOR_RGB: { 607 | target = getGray32("StackRegTarget", imp, colorWeights); 608 | break; 609 | } 610 | case ImagePlus.GRAY8: 611 | case ImagePlus.GRAY16: 612 | case ImagePlus.GRAY32: { 613 | target.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 614 | break; 615 | } 616 | default: { 617 | IJ.error("Unexpected image type"); 618 | return 0; 619 | } 620 | } 621 | } 622 | for (int s = targetSlice + 1; (s <= imp.getStackSize()); s++) { 623 | if (twoStackAlign){ 624 | globalTransform[0][0] = globalTransform[1][1] = globalTransform[2][2] = 1.0; 625 | globalTransform[0][1] = globalTransform[0][2] = globalTransform[1][0] = 0.0; 626 | globalTransform[1][2] = globalTransform[2][0] = globalTransform[2][1] = 0.0; 627 | target = getSlice(imp,s); 628 | if (!loadBool) 629 | source = registerSlice(source, target, tgtImg, width, height, 630 | transformation, globalTransform, anchorPoints, colorWeights, s); 631 | else 632 | source = registerSlice(source, target, imp, width, height, 633 | transformation, globalTransform, anchorPoints, colorWeights, s); 634 | if (source == null) return 2; 635 | }else{ 636 | if (loadSingleMatrix){ //with one transformation only, we need to reset the global transform each time 637 | globalTransform[0][0] = globalTransform[1][1] = globalTransform[2][2] = 1.0; 638 | globalTransform[0][1] = globalTransform[0][2] = globalTransform[1][0] = 0.0; 639 | globalTransform[1][2] = globalTransform[2][0] = globalTransform[2][1] = 0.0; 640 | } 641 | source = registerSlice(source, target, imp, width, height, 642 | transformation, globalTransform, anchorPoints, colorWeights, s); 643 | if (source == null) return 2; 644 | } 645 | } 646 | imp.setSlice(targetSlice); 647 | imp.updateAndDraw(); 648 | return 1; 649 | } 650 | 651 | 652 | 653 | private ImagePlus getSlice(ImagePlus imp, int index){ 654 | final int width = imp.getWidth(); 655 | final int height = imp.getHeight(); 656 | ImagePlus out = null; 657 | imp.setSlice(index); 658 | double[] colorWeights = null; 659 | switch (imp.getType()) { 660 | case ImagePlus.COLOR_256: 661 | case ImagePlus.COLOR_RGB:{ 662 | out = getGray32("StackRegTarget", imp, colorWeights); 663 | break; 664 | } 665 | case ImagePlus.GRAY8: { 666 | out = new ImagePlus("StackRegTarget", 667 | new ByteProcessor(width, height, new byte[width * height], 668 | imp.getProcessor().getColorModel())); 669 | out.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 670 | break; 671 | } 672 | case ImagePlus.GRAY16: { 673 | out = new ImagePlus("StackRegTarget", 674 | new ShortProcessor(width, height, new short[width * height], 675 | imp.getProcessor().getColorModel())); 676 | out.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 677 | break; 678 | } 679 | case ImagePlus.GRAY32: { 680 | out = new ImagePlus("StackRegTarget", 681 | new FloatProcessor(width, height, new float[width * height], 682 | imp.getProcessor().getColorModel())); 683 | out.getProcessor().copyBits(imp.getProcessor(), 0, 0, Blitter.COPY); 684 | break; 685 | } 686 | default: { 687 | IJ.error("Unexpected image type"); 688 | return null; 689 | } 690 | } 691 | return out; 692 | } 693 | 694 | /*------------------------------------------------------------------*/ 695 | //This is kind of an overloaded function. 696 | //'Sgot three different actions it can do, 697 | //and some of these vary depending on the file loaded. 698 | // 699 | private int loadTransform(int action, double[][] src, double[][] tgt){ 700 | try{ 701 | final FileReader fr=new FileReader(loadPathAndFilename); 702 | BufferedReader br = new BufferedReader(fr); 703 | String record; 704 | int separatorIndex; 705 | String[] fields=new String[3]; 706 | //src=new double[2][3]; 707 | //tgt=new double[2][3]; 708 | 709 | switch (action){ 710 | case 0:{ //return the index of the former target image, or detect if the 711 | //selected file contains only one transformation matrix and start from the 1st 712 | record = br.readLine(); 713 | record = record.trim(); 714 | if (record.equals("Transformation")) 715 | { 716 | loadSingleMatrix = true; 717 | fr.close(); 718 | return 1; 719 | }else{ 720 | loadSingleMatrix = false; 721 | } 722 | record = br.readLine(); 723 | record = br.readLine(); 724 | record = br.readLine(); 725 | record = br.readLine(); 726 | record = record.trim(); 727 | separatorIndex = record.indexOf("Target img: "); 728 | fields[0] = record.substring(separatorIndex+11).trim(); 729 | fr.close(); 730 | return (new Integer(fields[0])).intValue(); 731 | } 732 | case 1:{ //return the transform used and set twoStack boolean if needed 733 | int transformation=3; 734 | if (loadSingleMatrix){ 735 | record = br.readLine(); 736 | record = br.readLine(); 737 | record = record.trim(); 738 | if (record.equals("TRANSLATION")) { 739 | transformation = 0; 740 | } 741 | else if (record.equals("RIGID_BODY")) { 742 | transformation = 1; 743 | } 744 | else if (record.equals("SCALED_ROTATION")) { 745 | transformation = 2; 746 | } 747 | else if (record.equals("AFFINE")) { 748 | transformation = 3; 749 | } 750 | twoStackAlign=false; 751 | fr.close(); 752 | }else{ 753 | record = br.readLine(); 754 | record = br.readLine(); 755 | record = br.readLine(); 756 | int discardGlobal=(new Integer(record.trim())).intValue(); 757 | if (discardGlobal==1) 758 | twoStackAlign=true; 759 | else 760 | twoStackAlign=false; 761 | record = br.readLine(); 762 | record = record.trim(); 763 | if (record.equals("TRANSLATION")) { 764 | transformation = 0; 765 | } 766 | else if (record.equals("RIGID_BODY")) { 767 | transformation = 1; 768 | } 769 | else if (record.equals("SCALED_ROTATION")) { 770 | transformation = 2; 771 | } 772 | else if (record.equals("AFFINE")) { 773 | transformation = 3; 774 | } 775 | fr.close(); 776 | } 777 | return transformation; 778 | } 779 | case 2:{ //return the next transformation in src and tgt, the next src index as return value 780 | int rtnvalue = -1; 781 | if (loadSingleMatrix){ 782 | for (int j=0;j<10;j++) 783 | record = br.readLine(); 784 | for (int i=0;i<3;i++){ 785 | record = br.readLine(); 786 | record = record.trim(); 787 | separatorIndex = record.indexOf('\t'); 788 | fields[0] = record.substring(0, separatorIndex); 789 | fields[1] = record.substring(separatorIndex); 790 | fields[0] = fields[0].trim(); 791 | fields[1] = fields[1].trim(); 792 | src[i][0]=(new Double(fields[0])).doubleValue(); 793 | src[i][1]=(new Double(fields[1])).doubleValue(); 794 | } 795 | record = br.readLine(); 796 | record = br.readLine(); 797 | for (int i=0;i<3;i++){ 798 | record = br.readLine(); 799 | record = record.trim(); 800 | separatorIndex = record.indexOf('\t'); 801 | 802 | fields[0] = record.substring(0, separatorIndex); 803 | fields[1] = record.substring(separatorIndex); 804 | fields[0] = fields[0].trim(); 805 | fields[1] = fields[1].trim(); 806 | tgt[i][0]=(new Double(fields[0])).doubleValue(); 807 | tgt[i][1]=(new Double(fields[1])).doubleValue(); 808 | } 809 | 810 | }else{ 811 | record = br.readLine(); 812 | record = br.readLine(); 813 | record = br.readLine(); 814 | for (int i=0;i>> 16); 954 | g = (double)((pixels[k] & 0x0000FF00) >>> 8); 955 | b = (double)(pixels[k] & 0x000000FF); 956 | average[0] += r; 957 | average[1] += g; 958 | average[2] += b; 959 | scatterMatrix[0][0] += r * r; 960 | scatterMatrix[0][1] += r * g; 961 | scatterMatrix[0][2] += r * b; 962 | scatterMatrix[1][1] += g * g; 963 | scatterMatrix[1][2] += g * b; 964 | scatterMatrix[2][2] += b * b; 965 | } 966 | } 967 | } 968 | else { 969 | IJ.error("Internal type mismatch"); 970 | } 971 | length *= imp.getStackSize(); 972 | average[0] /= (double)length; 973 | average[1] /= (double)length; 974 | average[2] /= (double)length; 975 | scatterMatrix[0][0] /= (double)length; 976 | scatterMatrix[0][1] /= (double)length; 977 | scatterMatrix[0][2] /= (double)length; 978 | scatterMatrix[1][1] /= (double)length; 979 | scatterMatrix[1][2] /= (double)length; 980 | scatterMatrix[2][2] /= (double)length; 981 | scatterMatrix[0][0] -= average[0] * average[0]; 982 | scatterMatrix[0][1] -= average[0] * average[1]; 983 | scatterMatrix[0][2] -= average[0] * average[2]; 984 | scatterMatrix[1][1] -= average[1] * average[1]; 985 | scatterMatrix[1][2] -= average[1] * average[2]; 986 | scatterMatrix[2][2] -= average[2] * average[2]; 987 | scatterMatrix[2][1] = scatterMatrix[1][2]; 988 | scatterMatrix[2][0] = scatterMatrix[0][2]; 989 | scatterMatrix[1][0] = scatterMatrix[0][1]; 990 | } /* computeStatistics */ 991 | 992 | /*------------------------------------------------------------------*/ 993 | private double[] getColorWeightsFromPrincipalComponents ( 994 | final ImagePlus imp 995 | ) { 996 | final double[] average = {0.0, 0.0, 0.0}; 997 | final double[][] scatterMatrix = {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}}; 998 | computeStatistics(imp, average, scatterMatrix); 999 | double[] eigenvalue = getEigenvalues(scatterMatrix); 1000 | if ((eigenvalue[0] * eigenvalue[0] + eigenvalue[1] * eigenvalue[1] 1001 | + eigenvalue[2] * eigenvalue[2]) <= TINY) { 1002 | return(getLuminanceFromCCIR601()); 1003 | } 1004 | double bestEigenvalue = getLargestAbsoluteEigenvalue(eigenvalue); 1005 | double eigenvector[] = getEigenvector(scatterMatrix, bestEigenvalue); 1006 | final double weight = eigenvector[0] + eigenvector[1] + eigenvector[2]; 1007 | if (TINY < Math.abs(weight)) { 1008 | eigenvector[0] /= weight; 1009 | eigenvector[1] /= weight; 1010 | eigenvector[2] /= weight; 1011 | } 1012 | return(eigenvector); 1013 | } /* getColorWeightsFromPrincipalComponents */ 1014 | 1015 | /*------------------------------------------------------------------*/ 1016 | private double[] getEigenvalues ( 1017 | final double[][] scatterMatrix 1018 | ) { 1019 | final double[] a = { 1020 | scatterMatrix[0][0] * scatterMatrix[1][1] * scatterMatrix[2][2] 1021 | + 2.0 * scatterMatrix[0][1] * scatterMatrix[1][2] * scatterMatrix[2][0] 1022 | - scatterMatrix[0][1] * scatterMatrix[0][1] * scatterMatrix[2][2] 1023 | - scatterMatrix[1][2] * scatterMatrix[1][2] * scatterMatrix[0][0] 1024 | - scatterMatrix[2][0] * scatterMatrix[2][0] * scatterMatrix[1][1], 1025 | scatterMatrix[0][1] * scatterMatrix[0][1] 1026 | + scatterMatrix[1][2] * scatterMatrix[1][2] 1027 | + scatterMatrix[2][0] * scatterMatrix[2][0] 1028 | - scatterMatrix[0][0] * scatterMatrix[1][1] 1029 | - scatterMatrix[1][1] * scatterMatrix[2][2] 1030 | - scatterMatrix[2][2] * scatterMatrix[0][0], 1031 | scatterMatrix[0][0] + scatterMatrix[1][1] + scatterMatrix[2][2], 1032 | -1.0 1033 | }; 1034 | double[] RealRoot = new double[3]; 1035 | double Q = (3.0 * a[1] - a[2] * a[2] / a[3]) / (9.0 * a[3]); 1036 | double R = (a[1] * a[2] - 3.0 * a[0] * a[3] - (2.0 / 9.0) * a[2] * a[2] * a[2] / a[3]) 1037 | / (6.0 * a[3] * a[3]); 1038 | double Det = Q * Q * Q + R * R; 1039 | if (Det < 0.0) { 1040 | Det = 2.0 * Math.sqrt(-Q); 1041 | R /= Math.sqrt(-Q * Q * Q); 1042 | R = (1.0 / 3.0) * Math.acos(R); 1043 | Q = (1.0 / 3.0) * a[2] / a[3]; 1044 | RealRoot[0] = Det * Math.cos(R) - Q; 1045 | RealRoot[1] = Det * Math.cos(R + (2.0 / 3.0) * Math.PI) - Q; 1046 | RealRoot[2] = Det * Math.cos(R + (4.0 / 3.0) * Math.PI) - Q; 1047 | if (RealRoot[0] < RealRoot[1]) { 1048 | if (RealRoot[2] < RealRoot[1]) { 1049 | double Swap = RealRoot[1]; 1050 | RealRoot[1] = RealRoot[2]; 1051 | RealRoot[2] = Swap; 1052 | if (RealRoot[1] < RealRoot[0]) { 1053 | Swap = RealRoot[0]; 1054 | RealRoot[0] = RealRoot[1]; 1055 | RealRoot[1] = Swap; 1056 | } 1057 | } 1058 | } 1059 | else { 1060 | double Swap = RealRoot[0]; 1061 | RealRoot[0] = RealRoot[1]; 1062 | RealRoot[1] = Swap; 1063 | if (RealRoot[2] < RealRoot[1]) { 1064 | Swap = RealRoot[1]; 1065 | RealRoot[1] = RealRoot[2]; 1066 | RealRoot[2] = Swap; 1067 | if (RealRoot[1] < RealRoot[0]) { 1068 | Swap = RealRoot[0]; 1069 | RealRoot[0] = RealRoot[1]; 1070 | RealRoot[1] = Swap; 1071 | } 1072 | } 1073 | } 1074 | } 1075 | else if (Det == 0.0) { 1076 | final double P = 2.0 * ((R < 0.0) ? (Math.pow(-R, 1.0 / 3.0)) : (Math.pow(R, 1.0 / 3.0))); 1077 | Q = (1.0 / 3.0) * a[2] / a[3]; 1078 | if (P < 0) { 1079 | RealRoot[0] = P - Q; 1080 | RealRoot[1] = -0.5 * P - Q; 1081 | RealRoot[2] = RealRoot[1]; 1082 | } 1083 | else { 1084 | RealRoot[0] = -0.5 * P - Q; 1085 | RealRoot[1] = RealRoot[0]; 1086 | RealRoot[2] = P - Q; 1087 | } 1088 | } 1089 | else { 1090 | IJ.error("Warning: complex eigenvalue found; ignoring imaginary part."); 1091 | Det = Math.sqrt(Det); 1092 | Q = ((R + Det) < 0.0) ? (-Math.exp((1.0 / 3.0) * Math.log(-R - Det))) 1093 | : (Math.exp((1.0 / 3.0) * Math.log(R + Det))); 1094 | R = Q + ((R < Det) ? (-Math.exp((1.0 / 3.0) * Math.log(Det - R))) 1095 | : (Math.exp((1.0 / 3.0) * Math.log(R - Det)))); 1096 | Q = (-1.0 / 3.0) * a[2] / a[3]; 1097 | Det = Q + R; 1098 | RealRoot[0] = Q - R / 2.0; 1099 | RealRoot[1] = RealRoot[0]; 1100 | RealRoot[2] = RealRoot[1]; 1101 | if (Det < RealRoot[0]) { 1102 | RealRoot[0] = Det; 1103 | } 1104 | else { 1105 | RealRoot[2] = Det; 1106 | } 1107 | } 1108 | return(RealRoot); 1109 | } /* end getEigenvalues */ 1110 | 1111 | /*------------------------------------------------------------------*/ 1112 | private double[] getEigenvector ( 1113 | final double[][] scatterMatrix, 1114 | final double eigenvalue 1115 | ) { 1116 | final int n = scatterMatrix.length; 1117 | final double[][] matrix = new double[n][n]; 1118 | for (int i = 0; (i < n); i++) { 1119 | System.arraycopy(scatterMatrix[i], 0, matrix[i], 0, n); 1120 | matrix[i][i] -= eigenvalue; 1121 | } 1122 | final double[] eigenvector = new double[n]; 1123 | double absMax; 1124 | double max; 1125 | double norm; 1126 | for (int i = 0; (i < n); i++) { 1127 | norm = 0.0; 1128 | for (int j = 0; (j < n); j++) { 1129 | norm += matrix[i][j] * matrix[i][j]; 1130 | } 1131 | norm = Math.sqrt(norm); 1132 | if (TINY < norm) { 1133 | for (int j = 0; (j < n); j++) { 1134 | matrix[i][j] /= norm; 1135 | } 1136 | } 1137 | } 1138 | for (int j = 0; (j < n); j++) { 1139 | max = matrix[j][j]; 1140 | absMax = Math.abs(max); 1141 | int k = j; 1142 | for (int i = j + 1; (i < n); i++) { 1143 | if (absMax < Math.abs(matrix[i][j])) { 1144 | max = matrix[i][j]; 1145 | absMax = Math.abs(max); 1146 | k = i; 1147 | } 1148 | } 1149 | if (k != j) { 1150 | final double[] partialLine = new double[n - j]; 1151 | System.arraycopy(matrix[j], j, partialLine, 0, n - j); 1152 | System.arraycopy(matrix[k], j, matrix[j], j, n - j); 1153 | System.arraycopy(partialLine, 0, matrix[k], j, n - j); 1154 | } 1155 | if (TINY < absMax) { 1156 | for (k = 0; (k < n); k++) { 1157 | matrix[j][k] /= max; 1158 | } 1159 | } 1160 | for (int i = j + 1; (i < n); i++) { 1161 | max = matrix[i][j]; 1162 | for (k = 0; (k < n); k++) { 1163 | matrix[i][k] -= max * matrix[j][k]; 1164 | } 1165 | } 1166 | } 1167 | final boolean[] ignore = new boolean[n]; 1168 | int valid = n; 1169 | for (int i = 0; (i < n); i++) { 1170 | ignore[i] = false; 1171 | if (Math.abs(matrix[i][i]) < TINY) { 1172 | ignore[i] = true; 1173 | valid--; 1174 | eigenvector[i] = 1.0; 1175 | continue; 1176 | } 1177 | if (TINY < Math.abs(matrix[i][i] - 1.0)) { 1178 | IJ.error("Insufficient accuracy."); 1179 | eigenvector[0] = 0.212671; 1180 | eigenvector[1] = 0.71516; 1181 | eigenvector[2] = 0.072169; 1182 | return(eigenvector); 1183 | } 1184 | norm = 0.0; 1185 | for (int j = 0; (j < i); j++) { 1186 | norm += matrix[i][j] * matrix[i][j]; 1187 | } 1188 | for (int j = i + 1; (j < n); j++) { 1189 | norm += matrix[i][j] * matrix[i][j]; 1190 | } 1191 | if (Math.sqrt(norm) < TINY) { 1192 | ignore[i] = true; 1193 | valid--; 1194 | eigenvector[i] = 0.0; 1195 | continue; 1196 | } 1197 | } 1198 | if (0 < valid) { 1199 | double[][] reducedMatrix = new double[valid][valid]; 1200 | for (int i = 0, u = 0; (i < n); i++) { 1201 | if (!ignore[i]) { 1202 | for (int j = 0, v = 0; (j < n); j++) { 1203 | if (!ignore[j]) { 1204 | reducedMatrix[u][v] = matrix[i][j]; 1205 | v++; 1206 | } 1207 | } 1208 | u++; 1209 | } 1210 | } 1211 | double[] reducedEigenvector = new double[valid]; 1212 | for (int i = 0, u = 0; (i < n); i++) { 1213 | if (!ignore[i]) { 1214 | for (int j = 0; (j < n); j++) { 1215 | if (ignore[j]) { 1216 | reducedEigenvector[u] -= matrix[i][j] * eigenvector[j]; 1217 | } 1218 | } 1219 | u++; 1220 | } 1221 | } 1222 | reducedEigenvector = linearLeastSquares(reducedMatrix, reducedEigenvector); 1223 | for (int i = 0, u = 0; (i < n); i++) { 1224 | if (!ignore[i]) { 1225 | eigenvector[i] = reducedEigenvector[u]; 1226 | u++; 1227 | } 1228 | } 1229 | } 1230 | norm = 0.0; 1231 | for (int i = 0; (i < n); i++) { 1232 | norm += eigenvector[i] * eigenvector[i]; 1233 | } 1234 | norm = Math.sqrt(norm); 1235 | if (Math.sqrt(norm) < TINY) { 1236 | IJ.error("Insufficient accuracy."); 1237 | eigenvector[0] = 0.212671; 1238 | eigenvector[1] = 0.71516; 1239 | eigenvector[2] = 0.072169; 1240 | return(eigenvector); 1241 | } 1242 | absMax = Math.abs(eigenvector[0]); 1243 | valid = 0; 1244 | for (int i = 1; (i < n); i++) { 1245 | max = Math.abs(eigenvector[i]); 1246 | if (absMax < max) { 1247 | absMax = max; 1248 | valid = i; 1249 | } 1250 | } 1251 | norm = (eigenvector[valid] < 0.0) ? (-norm) : (norm); 1252 | for (int i = 0; (i < n); i++) { 1253 | eigenvector[i] /= norm; 1254 | } 1255 | return(eigenvector); 1256 | } /* getEigenvector */ 1257 | 1258 | /*------------------------------------------------------------------*/ 1259 | private ImagePlus getGray32 ( 1260 | final String title, 1261 | final ImagePlus imp, 1262 | final double[] colorWeights 1263 | ) { 1264 | final int length = imp.getWidth() * imp.getHeight(); 1265 | final ImagePlus gray32 = new ImagePlus(title, 1266 | new FloatProcessor(imp.getWidth(), imp.getHeight())); 1267 | final float[] gray = (float[])gray32.getProcessor().getPixels(); 1268 | double r; 1269 | double g; 1270 | double b; 1271 | if (imp.getProcessor().getPixels() instanceof byte[]) { 1272 | final byte[] pixels = (byte[])imp.getProcessor().getPixels(); 1273 | final IndexColorModel icm = (IndexColorModel)imp.getProcessor().getColorModel(); 1274 | final int mapSize = icm.getMapSize(); 1275 | final byte[] reds = new byte[mapSize]; 1276 | final byte[] greens = new byte[mapSize]; 1277 | final byte[] blues = new byte[mapSize]; 1278 | icm.getReds(reds); 1279 | icm.getGreens(greens); 1280 | icm.getBlues(blues); 1281 | int index; 1282 | for (int k = 0; (k < length); k++) { 1283 | index = (int)(pixels[k] & 0xFF); 1284 | r = (double)(reds[index] & 0xFF); 1285 | g = (double)(greens[index] & 0xFF); 1286 | b = (double)(blues[index] & 0xFF); 1287 | gray[k] = (float)(colorWeights[0] * r + colorWeights[1] * g + colorWeights[2] * b); 1288 | } 1289 | } 1290 | else if (imp.getProcessor().getPixels() instanceof int[]) { 1291 | final int[] pixels = (int[])imp.getProcessor().getPixels(); 1292 | for (int k = 0; (k < length); k++) { 1293 | r = (double)((pixels[k] & 0x00FF0000) >>> 16); 1294 | g = (double)((pixels[k] & 0x0000FF00) >>> 8); 1295 | b = (double)(pixels[k] & 0x000000FF); 1296 | gray[k] = (float)(colorWeights[0] * r + colorWeights[1] * g + colorWeights[2] * b); 1297 | } 1298 | } 1299 | return(gray32); 1300 | } /* getGray32 */ 1301 | 1302 | /*------------------------------------------------------------------*/ 1303 | private double getLargestAbsoluteEigenvalue ( 1304 | final double[] eigenvalue 1305 | ) { 1306 | double best = eigenvalue[0]; 1307 | for (int k = 1; (k < eigenvalue.length); k++) { 1308 | if (Math.abs(best) < Math.abs(eigenvalue[k])) { 1309 | best = eigenvalue[k]; 1310 | } 1311 | if (Math.abs(best) == Math.abs(eigenvalue[k])) { 1312 | if (best < eigenvalue[k]) { 1313 | best = eigenvalue[k]; 1314 | } 1315 | } 1316 | } 1317 | return(best); 1318 | } /* getLargestAbsoluteEigenvalue */ 1319 | 1320 | /*------------------------------------------------------------------*/ 1321 | private double[] getLuminanceFromCCIR601 ( 1322 | ) { 1323 | double[] weights = {0.299, 0.587, 0.114}; 1324 | return(weights); 1325 | } /* getLuminanceFromCCIR601 */ 1326 | 1327 | /*------------------------------------------------------------------*/ 1328 | private double[][] getTransformationMatrix ( 1329 | final double[][] fromCoord, 1330 | final double[][] toCoord, 1331 | final int transformation 1332 | ) { 1333 | double[][] matrix = new double[3][3]; 1334 | switch (transformation) { 1335 | case 0: { 1336 | matrix[0][0] = 1.0; 1337 | matrix[0][1] = 0.0; 1338 | matrix[0][2] = toCoord[0][0] - fromCoord[0][0]; 1339 | matrix[1][0] = 0.0; 1340 | matrix[1][1] = 1.0; 1341 | matrix[1][2] = toCoord[0][1] - fromCoord[0][1]; 1342 | break; 1343 | } 1344 | case 1: { 1345 | final double angle = Math.atan2(fromCoord[2][0] - fromCoord[1][0], 1346 | fromCoord[2][1] - fromCoord[1][1]) - Math.atan2(toCoord[2][0] - toCoord[1][0], 1347 | toCoord[2][1] - toCoord[1][1]); 1348 | final double c = Math.cos(angle); 1349 | final double s = Math.sin(angle); 1350 | matrix[0][0] = c; 1351 | matrix[0][1] = -s; 1352 | matrix[0][2] = toCoord[0][0] - c * fromCoord[0][0] + s * fromCoord[0][1]; 1353 | matrix[1][0] = s; 1354 | matrix[1][1] = c; 1355 | matrix[1][2] = toCoord[0][1] - s * fromCoord[0][0] - c * fromCoord[0][1]; 1356 | break; 1357 | } 1358 | case 2: { 1359 | double[][] a = new double[3][3]; 1360 | double[] v = new double[3]; 1361 | a[0][0] = fromCoord[0][0]; 1362 | a[0][1] = fromCoord[0][1]; 1363 | a[0][2] = 1.0; 1364 | a[1][0] = fromCoord[1][0]; 1365 | a[1][1] = fromCoord[1][1]; 1366 | a[1][2] = 1.0; 1367 | a[2][0] = fromCoord[0][1] - fromCoord[1][1] + fromCoord[1][0]; 1368 | a[2][1] = fromCoord[1][0] + fromCoord[1][1] - fromCoord[0][0]; 1369 | a[2][2] = 1.0; 1370 | invertGauss(a); 1371 | v[0] = toCoord[0][0]; 1372 | v[1] = toCoord[1][0]; 1373 | v[2] = toCoord[0][1] - toCoord[1][1] + toCoord[1][0]; 1374 | for (int i = 0; (i < 3); i++) { 1375 | matrix[0][i] = 0.0; 1376 | for (int j = 0; (j < 3); j++) { 1377 | matrix[0][i] += a[i][j] * v[j]; 1378 | } 1379 | } 1380 | v[0] = toCoord[0][1]; 1381 | v[1] = toCoord[1][1]; 1382 | v[2] = toCoord[1][0] + toCoord[1][1] - toCoord[0][0]; 1383 | for (int i = 0; (i < 3); i++) { 1384 | matrix[1][i] = 0.0; 1385 | for (int j = 0; (j < 3); j++) { 1386 | matrix[1][i] += a[i][j] * v[j]; 1387 | } 1388 | } 1389 | break; 1390 | } 1391 | case 3: { 1392 | double[][] a = new double[3][3]; 1393 | double[] v = new double[3]; 1394 | a[0][0] = fromCoord[0][0]; 1395 | a[0][1] = fromCoord[0][1]; 1396 | a[0][2] = 1.0; 1397 | a[1][0] = fromCoord[1][0]; 1398 | a[1][1] = fromCoord[1][1]; 1399 | a[1][2] = 1.0; 1400 | a[2][0] = fromCoord[2][0]; 1401 | a[2][1] = fromCoord[2][1]; 1402 | a[2][2] = 1.0; 1403 | invertGauss(a); 1404 | v[0] = toCoord[0][0]; 1405 | v[1] = toCoord[1][0]; 1406 | v[2] = toCoord[2][0]; 1407 | for (int i = 0; (i < 3); i++) { 1408 | matrix[0][i] = 0.0; 1409 | for (int j = 0; (j < 3); j++) { 1410 | matrix[0][i] += a[i][j] * v[j]; 1411 | } 1412 | } 1413 | v[0] = toCoord[0][1]; 1414 | v[1] = toCoord[1][1]; 1415 | v[2] = toCoord[2][1]; 1416 | for (int i = 0; (i < 3); i++) { 1417 | matrix[1][i] = 0.0; 1418 | for (int j = 0; (j < 3); j++) { 1419 | matrix[1][i] += a[i][j] * v[j]; 1420 | } 1421 | } 1422 | break; 1423 | } 1424 | default: { 1425 | IJ.error("Unexpected transformation"); 1426 | } 1427 | } 1428 | matrix[2][0] = 0.0; 1429 | matrix[2][1] = 0.0; 1430 | matrix[2][2] = 1.0; 1431 | return(matrix); 1432 | } /* end getTransformationMatrix */ 1433 | 1434 | /*------------------------------------------------------------------*/ 1435 | private void invertGauss ( 1436 | final double[][] matrix 1437 | ) { 1438 | final int n = matrix.length; 1439 | final double[][] inverse = new double[n][n]; 1440 | for (int i = 0; (i < n); i++) { 1441 | double max = matrix[i][0]; 1442 | double absMax = Math.abs(max); 1443 | for (int j = 0; (j < n); j++) { 1444 | inverse[i][j] = 0.0; 1445 | if (absMax < Math.abs(matrix[i][j])) { 1446 | max = matrix[i][j]; 1447 | absMax = Math.abs(max); 1448 | } 1449 | } 1450 | inverse[i][i] = 1.0 / max; 1451 | for (int j = 0; (j < n); j++) { 1452 | matrix[i][j] /= max; 1453 | } 1454 | } 1455 | for (int j = 0; (j < n); j++) { 1456 | double max = matrix[j][j]; 1457 | double absMax = Math.abs(max); 1458 | int k = j; 1459 | for (int i = j + 1; (i < n); i++) { 1460 | if (absMax < Math.abs(matrix[i][j])) { 1461 | max = matrix[i][j]; 1462 | absMax = Math.abs(max); 1463 | k = i; 1464 | } 1465 | } 1466 | if (k != j) { 1467 | final double[] partialLine = new double[n - j]; 1468 | final double[] fullLine = new double[n]; 1469 | System.arraycopy(matrix[j], j, partialLine, 0, n - j); 1470 | System.arraycopy(matrix[k], j, matrix[j], j, n - j); 1471 | System.arraycopy(partialLine, 0, matrix[k], j, n - j); 1472 | System.arraycopy(inverse[j], 0, fullLine, 0, n); 1473 | System.arraycopy(inverse[k], 0, inverse[j], 0, n); 1474 | System.arraycopy(fullLine, 0, inverse[k], 0, n); 1475 | } 1476 | for (k = 0; (k <= j); k++) { 1477 | inverse[j][k] /= max; 1478 | } 1479 | for (k = j + 1; (k < n); k++) { 1480 | matrix[j][k] /= max; 1481 | inverse[j][k] /= max; 1482 | } 1483 | for (int i = j + 1; (i < n); i++) { 1484 | for (k = 0; (k <= j); k++) { 1485 | inverse[i][k] -= matrix[i][j] * inverse[j][k]; 1486 | } 1487 | for (k = j + 1; (k < n); k++) { 1488 | matrix[i][k] -= matrix[i][j] * matrix[j][k]; 1489 | inverse[i][k] -= matrix[i][j] * inverse[j][k]; 1490 | } 1491 | } 1492 | } 1493 | for (int j = n - 1; (1 <= j); j--) { 1494 | for (int i = j - 1; (0 <= i); i--) { 1495 | for (int k = 0; (k <= j); k++) { 1496 | inverse[i][k] -= matrix[i][j] * inverse[j][k]; 1497 | } 1498 | for (int k = j + 1; (k < n); k++) { 1499 | matrix[i][k] -= matrix[i][j] * matrix[j][k]; 1500 | inverse[i][k] -= matrix[i][j] * inverse[j][k]; 1501 | } 1502 | } 1503 | } 1504 | for (int i = 0; (i < n); i++) { 1505 | System.arraycopy(inverse[i], 0, matrix[i], 0, n); 1506 | } 1507 | } /* end invertGauss */ 1508 | 1509 | /*------------------------------------------------------------------*/ 1510 | private double[] linearLeastSquares ( 1511 | final double[][] A, 1512 | final double[] b 1513 | ) { 1514 | final int lines = A.length; 1515 | final int columns = A[0].length; 1516 | final double[][] Q = new double[lines][columns]; 1517 | final double[][] R = new double[columns][columns]; 1518 | final double[] x = new double[columns]; 1519 | double s; 1520 | for (int i = 0; (i < lines); i++) { 1521 | for (int j = 0; (j < columns); j++) { 1522 | Q[i][j] = A[i][j]; 1523 | } 1524 | } 1525 | QRdecomposition(Q, R); 1526 | for (int i = 0; (i < columns); i++) { 1527 | s = 0.0; 1528 | for (int j = 0; (j < lines); j++) { 1529 | s += Q[j][i] * b[j]; 1530 | } 1531 | x[i] = s; 1532 | } 1533 | for (int i = columns - 1; (0 <= i); i--) { 1534 | s = R[i][i]; 1535 | if ((s * s) == 0.0) { 1536 | x[i] = 0.0; 1537 | } 1538 | else { 1539 | x[i] /= s; 1540 | } 1541 | for (int j = i - 1; (0 <= j); j--) { 1542 | x[j] -= R[j][i] * x[i]; 1543 | } 1544 | } 1545 | return(x); 1546 | } /* end linearLeastSquares */ 1547 | 1548 | /*------------------------------------------------------------------*/ 1549 | private void QRdecomposition ( 1550 | final double[][] Q, 1551 | final double[][] R 1552 | ) { 1553 | final int lines = Q.length; 1554 | final int columns = Q[0].length; 1555 | final double[][] A = new double[lines][columns]; 1556 | double s; 1557 | for (int j = 0; (j < columns); j++) { 1558 | for (int i = 0; (i < lines); i++) { 1559 | A[i][j] = Q[i][j]; 1560 | } 1561 | for (int k = 0; (k < j); k++) { 1562 | s = 0.0; 1563 | for (int i = 0; (i < lines); i++) { 1564 | s += A[i][j] * Q[i][k]; 1565 | } 1566 | for (int i = 0; (i < lines); i++) { 1567 | Q[i][j] -= s * Q[i][k]; 1568 | } 1569 | } 1570 | s = 0.0; 1571 | for (int i = 0; (i < lines); i++) { 1572 | s += Q[i][j] * Q[i][j]; 1573 | } 1574 | if ((s * s) == 0.0) { 1575 | s = 0.0; 1576 | } 1577 | else { 1578 | s = 1.0 / Math.sqrt(s); 1579 | } 1580 | for (int i = 0; (i < lines); i++) { 1581 | Q[i][j] *= s; 1582 | } 1583 | } 1584 | for (int i = 0; (i < columns); i++) { 1585 | for (int j = 0; (j < i); j++) { 1586 | R[i][j] = 0.0; 1587 | } 1588 | for (int j = i; (j < columns); j++) { 1589 | R[i][j] = 0.0; 1590 | for (int k = 0; (k < lines); k++) { 1591 | R[i][j] += Q[k][i] * A[k][j]; 1592 | } 1593 | } 1594 | } 1595 | } /* end QRdecomposition */ 1596 | 1597 | // saves temporary slice image in temp directory. 1598 | String saveTempImageFile(ImagePlus sourceimp){ 1599 | FileSaver sourceFile = new FileSaver(sourceimp); 1600 | String sourcePathAndFileName = IJ.getDirectory("temp") + UUID.randomUUID().toString() + sourceimp.getTitle(); 1601 | sourceFile.saveAsTiff(sourcePathAndFileName); 1602 | return sourcePathAndFileName; 1603 | } 1604 | 1605 | // does Turboreg and returns truboreg instance. 1606 | Object doTurboReg(String sourcePathAndFileName, String sourcePointsText){ 1607 | Object turboReg; 1608 | try { 1609 | turboReg = IJ.runPlugIn("TurboReg_", "-transform" 1610 | + " -file " + sourcePathAndFileName 1611 | + sourcePointsText); 1612 | if (turboReg == null) { 1613 | throw(new ClassNotFoundException()); 1614 | } 1615 | } catch (ClassNotFoundException e) { 1616 | IJ.error("Please download TurboReg_ from\nhttp://bigwww.epfl.ch/thevenaz/turboreg/"); 1617 | return(null); 1618 | } 1619 | return turboReg; 1620 | } 1621 | 1622 | // generates a part of second arguments for TRANSLATION. 1623 | String generateSourcePointsText_translation(double[][] globalTransform, double[][] anchorPoints, int width, int height){ 1624 | double[][] sourcePoints = new double[1][3]; 1625 | for (int i = 0; (i < 3); i++) { 1626 | sourcePoints[0][i] = 0.0; 1627 | for (int j = 0; (j < 3); j++) { 1628 | sourcePoints[0][i] += globalTransform[i][j] 1629 | * anchorPoints[0][j]; 1630 | } 1631 | } 1632 | String sourcePointsText = " " + width + " " + height 1633 | + " -translation" 1634 | + " " + sourcePoints[0][0] + " " + sourcePoints[0][1] 1635 | + " " + (width / 2) + " " + (height / 2) 1636 | + " -hideOutput"; 1637 | return sourcePointsText; 1638 | } 1639 | 1640 | // generates a part of second arguments for RIGID_BODY. 1641 | String generateSourcePointsText_rigidBody(double[][] globalTransform, double[][] anchorPoints, int width, int height){ 1642 | double[][] sourcePoints = new double[3][3]; 1643 | for (int i = 0; (i < 3); i++) { 1644 | sourcePoints[0][i] = 0.0; 1645 | sourcePoints[1][i] = 0.0; 1646 | sourcePoints[2][i] = 0.0; 1647 | for (int j = 0; (j < 3); j++) { 1648 | sourcePoints[0][i] += globalTransform[i][j] 1649 | * anchorPoints[0][j]; 1650 | sourcePoints[1][i] += globalTransform[i][j] 1651 | * anchorPoints[1][j]; 1652 | sourcePoints[2][i] += globalTransform[i][j] 1653 | * anchorPoints[2][j]; 1654 | } 1655 | } 1656 | String sourcePointsText = " " + width + " " + height 1657 | + " -rigidBody" 1658 | + " " + sourcePoints[0][0] + " " + sourcePoints[0][1] 1659 | + " " + (width / 2) + " " + (height / 2) 1660 | + " " + sourcePoints[1][0] + " " + sourcePoints[1][1] 1661 | + " " + (width / 2) + " " + (height / 4) 1662 | + " " + sourcePoints[2][0] + " " + sourcePoints[2][1] 1663 | + " " + (width / 2) + " " + ((3 * height) / 4) 1664 | + " -hideOutput"; 1665 | return sourcePointsText; 1666 | } 1667 | // generates a part of second arguments for SCALED_ROTATION. 1668 | String generateSourcePointsText_scaledRotation(double[][] globalTransform, double[][] anchorPoints, int width, int height){ 1669 | double[][] sourcePoints = new double[2][3]; 1670 | for (int i = 0; (i < 3); i++) { 1671 | sourcePoints[0][i] = 0.0; 1672 | sourcePoints[1][i] = 0.0; 1673 | for (int j = 0; (j < 3); j++) { 1674 | sourcePoints[0][i] += globalTransform[i][j] 1675 | * anchorPoints[0][j]; 1676 | sourcePoints[1][i] += globalTransform[i][j] 1677 | * anchorPoints[1][j]; 1678 | } 1679 | } 1680 | String sourcePointsText = " " + width + " " + height 1681 | + " -scaledRotation" 1682 | + " " + sourcePoints[0][0] + " " + sourcePoints[0][1] 1683 | + " " + (width / 4) + " " + (height / 2) 1684 | + " " + sourcePoints[1][0] + " " + sourcePoints[1][1] 1685 | + " " + ((3 * width) / 4) + " " + (height / 2) 1686 | + " -hideOutput"; 1687 | return sourcePointsText; 1688 | } 1689 | // generates a part of second arguments for AFFINE. 1690 | String generateSourcePointsText_affine(double[][] globalTransform, double[][] anchorPoints, int width, int height){ 1691 | double[][] sourcePoints = new double[3][3]; 1692 | for (int i = 0; (i < 3); i++) { 1693 | sourcePoints[0][i] = 0.0; 1694 | sourcePoints[1][i] = 0.0; 1695 | sourcePoints[2][i] = 0.0; 1696 | for (int j = 0; (j < 3); j++) { 1697 | sourcePoints[0][i] += globalTransform[i][j] 1698 | * anchorPoints[0][j]; 1699 | sourcePoints[1][i] += globalTransform[i][j] 1700 | * anchorPoints[1][j]; 1701 | sourcePoints[2][i] += globalTransform[i][j] 1702 | * anchorPoints[2][j]; 1703 | } 1704 | } 1705 | String sourcePointsText = " " + width + " " + height 1706 | + " -affine" 1707 | + " " + sourcePoints[0][0] + " " + sourcePoints[0][1] 1708 | + " " + (width / 2) + " " + (height / 4) 1709 | + " " + sourcePoints[1][0] + " " + sourcePoints[1][1] 1710 | + " " + (width / 4) + " " + ((3 * height) / 4) 1711 | + " " + sourcePoints[2][0] + " " + sourcePoints[2][1] 1712 | + " " + ((3 * width) / 4) + " " + ((3 * height) / 4) 1713 | + " -hideOutput"; 1714 | return sourcePointsText; 1715 | } 1716 | 1717 | // generation of TurboReg 2nd argument, depending on 1718 | // four different types of transformation 1719 | String generateSourcePointsText(int transformation, double[][] globalTransform, double[][] anchorPoints, int width, int height){ 1720 | String sourcePointsText = ""; 1721 | if (transformation == 0) 1722 | sourcePointsText = generateSourcePointsText_translation( globalTransform, anchorPoints, width, height); 1723 | else if (transformation == 1) 1724 | sourcePointsText = generateSourcePointsText_rigidBody( globalTransform, anchorPoints, width, height); 1725 | else if (transformation == 2) 1726 | sourcePointsText = generateSourcePointsText_scaledRotation( globalTransform, anchorPoints, width, height); 1727 | else if (transformation == 3) 1728 | sourcePointsText = generateSourcePointsText_affine( globalTransform, anchorPoints, width, height); 1729 | else { 1730 | IJ.error("Unexpected transformation"); 1731 | return(null); 1732 | } 1733 | return sourcePointsText; 1734 | } 1735 | 1736 | // retrieves transformed image from turboReg instance. 1737 | // 1738 | ImagePlus retrieveTransformedImage(Object turboReg){ 1739 | Method method = null; 1740 | ImagePlus transformedSource = null; 1741 | try{ 1742 | try { 1743 | method = turboReg.getClass().getMethod("getTransformedImage", null); 1744 | } catch (NoSuchMethodException e){ 1745 | IJ.log(e.toString()); 1746 | } 1747 | try { 1748 | transformedSource = (ImagePlus)method.invoke(turboReg, null); 1749 | } catch (InvocationTargetException e){ 1750 | IJ.log(e.toString()); 1751 | } 1752 | } catch (IllegalAccessException e){ 1753 | IJ.log(e.toString()); 1754 | } 1755 | if (transformedSource != null){ 1756 | transformedSource.getStack().deleteLastSlice(); 1757 | transformedSource.getProcessor().setMinAndMax(0.0, 255.0); 1758 | ImageConverter converter = new ImageConverter(transformedSource); 1759 | converter.convertToGray8(); 1760 | } 1761 | return transformedSource; 1762 | } 1763 | 1764 | 1765 | /*-----------------------------------------------------------------*/ 1766 | @SuppressWarnings({ }) 1767 | private ImagePlus registerSlice ( 1768 | ImagePlus source, 1769 | ImagePlus target, 1770 | ImagePlus imp, 1771 | final int width, 1772 | final int height, 1773 | final int transformation, 1774 | final double[][] globalTransform, 1775 | final double[][] anchorPoints, 1776 | final double[] colorWeights, 1777 | int s 1778 | ) { 1779 | imp.setSlice(s); 1780 | try { 1781 | Object turboReg = null; 1782 | Method method = null; 1783 | double[][] sourcePoints = null; 1784 | double[][] targetPoints = null; 1785 | double[][] localTransform = null; 1786 | if (imp.getType() != ImagePlus.COLOR_256) { 1787 | if (imp.getType() == ImagePlus.COLOR_RGB) 1788 | source = getGray32("StackRegSource", imp, colorWeights); 1789 | else { 1790 | source = new ImagePlus("StackRegSource", imp.getProcessor()); 1791 | } 1792 | } 1793 | CsourcePathAndFileName = saveTempImageFile( source ); 1794 | CtargetPathAndFileName = saveTempImageFile( target ); 1795 | /* final FileSaver sourceFile = new FileSaver(source);*/ 1796 | //final String sourcePathAndFileName = IJ.getDirectory("temp") + UUID.randomUUID().toString() + source.getTitle(); 1797 | //sourceFile.saveAsTiff(sourcePathAndFileName); 1798 | /*CsourcePathAndFileName = sourcePathAndFileName;*/ 1799 | /* final FileSaver targetFile = new FileSaver(target);*/ 1800 | //final String targetPathAndFileName = IJ.getDirectory("temp") + UUID.randomUUID().toString() + target.getTitle(); 1801 | //targetFile.saveAsTiff(targetPathAndFileName); 1802 | /*CtargetPathAndFileName = targetPathAndFileName;*/ 1803 | String fileAssignments = " -file " + CsourcePathAndFileName 1804 | + " 0 0 " + (width - 1) + " " + (height - 1) 1805 | + " -file " + CtargetPathAndFileName 1806 | + " 0 0 " + (width - 1) + " " + (height - 1); 1807 | String turboRegSourcePoints = ""; 1808 | // if there is no transformation file specified, a transformation matrix should be computed. 1809 | // sampling points are arbiturally taken. Number of these points varies depending on the transformation mode. 1810 | if (loadPathAndFilename==""){ 1811 | switch (transformation) { 1812 | case 0: 1813 | { 1814 | turboRegSourcePoints = 1815 | " -translation" 1816 | + " " + (width / 2) + " " + (height / 2) 1817 | + " " + (width / 2) + " " + (height / 2); 1818 | break; 1819 | } 1820 | case 1: 1821 | { 1822 | turboRegSourcePoints = 1823 | " -rigidBody" 1824 | + " " + (width / 2) + " " + (height / 2) 1825 | + " " + (width / 2) + " " + (height / 2) 1826 | + " " + (width / 2) + " " + (height / 4) 1827 | + " " + (width / 2) + " " + (height / 4) 1828 | + " " + (width / 2) + " " + ((3 * height) / 4) 1829 | + " " + (width / 2) + " " + ((3 * height) / 4); 1830 | break; 1831 | } 1832 | case 2: 1833 | { 1834 | turboRegSourcePoints = 1835 | " -scaledRotation" 1836 | + " " + (width / 4) + " " + (height / 2) 1837 | + " " + (width / 4) + " " + (height / 2) 1838 | + " " + ((3 * width) / 4) + " " + (height / 2) 1839 | + " " + ((3 * width) / 4) + " " + (height / 2); 1840 | break; 1841 | } 1842 | case 3: 1843 | { 1844 | turboRegSourcePoints = 1845 | " -affine" 1846 | + " " + (width / 2) + " " + (height / 4) 1847 | + " " + (width / 2) + " " + (height / 4) 1848 | + " " + (width / 4) + " " + ((3 * height) / 4) 1849 | + " " + (width / 4) + " " + ((3 * height) / 4) 1850 | + " " + ((3 * width) / 4) + " " + ((3 * height) / 4) 1851 | + " " + ((3 * width) / 4) + " " + ((3 * height) / 4); 1852 | break; 1853 | } 1854 | default: 1855 | { 1856 | IJ.error("Unexpected transformation"); 1857 | deleteTempFiles(); 1858 | return(null); 1859 | } 1860 | 1861 | } 1862 | String secondParameter = 1863 | "-align" 1864 | + fileAssignments 1865 | + turboRegSourcePoints 1866 | + " -hideOutput"; 1867 | turboReg = IJ.runPlugIn("TurboReg_", secondParameter); 1868 | if (turboReg == null) { 1869 | throw(new ClassNotFoundException()); 1870 | } 1871 | target.setProcessor(null, source.getProcessor()); 1872 | method = turboReg.getClass().getMethod("getSourcePoints", null); 1873 | sourcePoints = ((double[][])method.invoke(turboReg, null)); 1874 | method = turboReg.getClass().getMethod("getTargetPoints", null); 1875 | targetPoints = ((double[][])method.invoke(turboReg, null)); 1876 | if (saveTransform) appendTransform(savePath+saveFile, s, tSlice,sourcePoints,targetPoints, transformation); 1877 | 1878 | } else { 1879 | 1880 | //if we've specified a transformation to load, we needen't bother with aligning them again 1881 | sourcePoints=new double[3][2]; 1882 | targetPoints=new double[3][2]; 1883 | int test= loadTransform(2, sourcePoints, targetPoints); 1884 | if (test != -1 && test != s){ 1885 | if (!twoStackAlign && !loadSingleMatrix && !fairlyWarned){ 1886 | IJ.error ("We've found some strangeness: the current transformation file index ("+test+") \n"+ 1887 | "and image index ("+s+") don't line up, which this type of alignment needs. \n"+ 1888 | "We'll proceed for now, but it may not work."); 1889 | fairlyWarned=true; 1890 | } 1891 | s=test; 1892 | imp.setSlice(s); 1893 | } 1894 | transformNumber++; 1895 | } 1896 | localTransform = getTransformationMatrix(targetPoints, sourcePoints, 1897 | transformation); 1898 | double[][] rescued = { 1899 | {globalTransform[0][0], globalTransform[0][1], globalTransform[0][2]}, 1900 | {globalTransform[1][0], globalTransform[1][1], globalTransform[1][2]}, 1901 | {globalTransform[2][0], globalTransform[2][1], globalTransform[2][2]} 1902 | }; 1903 | for (int i = 0; (i < 3); i++) { 1904 | for (int j = 0; (j < 3); j++) { 1905 | globalTransform[i][j] = 0.0; 1906 | for (int k = 0; (k < 3); k++) { 1907 | globalTransform[i][j] += localTransform[i][k] * rescued[k][j]; 1908 | } 1909 | } 1910 | } 1911 | String sourcePointsText = generateSourcePointsText( transformation, globalTransform, anchorPoints, width, height); 1912 | if (sourcePointsText == null){ 1913 | deleteTempFiles(); 1914 | return null; 1915 | } 1916 | 1917 | switch (imp.getType()) { 1918 | case ImagePlus.COLOR_256: 1919 | { 1920 | 1921 | source = new ImagePlus("StackRegSource", imp.getProcessor()); 1922 | ImageConverter converter = new ImageConverter(source); 1923 | converter.convertToRGB(); 1924 | ImagePlus[] rgbimps = ChannelSplitter.split(source); 1925 | ImagePlus sourceR = rgbimps[0]; 1926 | ImagePlus sourceG = rgbimps[1]; 1927 | ImagePlus sourceB = rgbimps[2]; 1928 | 1929 | /* Object turboRegR = null;*/ 1930 | //Object turboRegG = null; 1931 | //Object turboRegB = null; 1932 | 1933 | //ImagePlus transformedSourceR = null; 1934 | //ImagePlus transformedSourceG = null; 1935 | /*ImagePlus transformedSourceB = null;*/ 1936 | 1937 | CsourcePathAndFileNameR = saveTempImageFile(sourceR); 1938 | CsourcePathAndFileNameG = saveTempImageFile(sourceG); 1939 | CsourcePathAndFileNameB = saveTempImageFile(sourceB); 1940 | 1941 | Object turboRegR = doTurboReg(CsourcePathAndFileNameR, sourcePointsText); 1942 | Object turboRegG = doTurboReg(CsourcePathAndFileNameG, sourcePointsText); 1943 | Object turboRegB = doTurboReg(CsourcePathAndFileNameB, sourcePointsText); 1944 | 1945 | ImagePlus transformedSourceR = retrieveTransformedImage( turboRegR ); 1946 | ImagePlus transformedSourceG = retrieveTransformedImage( turboRegG ); 1947 | ImagePlus transformedSourceB = retrieveTransformedImage( turboRegB ); 1948 | 1949 | final IndexColorModel icm = (IndexColorModel)imp.getProcessor().getColorModel(); 1950 | final byte[] pixels = (byte[])imp.getProcessor().getPixels(); 1951 | byte[] r = new byte[width * height]; 1952 | byte[] g = new byte[width * height]; 1953 | byte[] b = new byte[width * height]; 1954 | r = (byte[])transformedSourceR.getProcessor().getPixels(); 1955 | 1956 | g = (byte[])transformedSourceG.getProcessor().getPixels(); 1957 | b = (byte[])transformedSourceB.getProcessor().getPixels(); 1958 | final int[] color = new int[4]; 1959 | color[3] = 255; 1960 | for (int k = 0; (k < pixels.length); k++) { 1961 | color[0] = (int)(r[k] & 0xFF); 1962 | color[1] = (int)(g[k] & 0xFF); 1963 | color[2] = (int)(b[k] & 0xFF); 1964 | pixels[k] = (byte)icm.getDataElement(color, 0); 1965 | } 1966 | break; 1967 | } 1968 | case ImagePlus.COLOR_RGB: 1969 | { 1970 | /* Object turboRegR = null;*/ 1971 | //Object turboRegG = null; 1972 | /*Object turboRegB = null;*/ 1973 | 1974 | ImagePlus[] rgbimps = ChannelSplitter.split( imp ); 1975 | ImagePlus sourceR = rgbimps[0]; 1976 | ImagePlus sourceG = rgbimps[1]; 1977 | ImagePlus sourceB = rgbimps[2]; 1978 | /* ImagePlus transformedSourceR = null;*/ 1979 | //ImagePlus transformedSourceG = null; 1980 | /*ImagePlus transformedSourceB = null;*/ 1981 | 1982 | CsourcePathAndFileNameR = saveTempImageFile(sourceR); 1983 | CsourcePathAndFileNameG = saveTempImageFile(sourceG); 1984 | CsourcePathAndFileNameB = saveTempImageFile(sourceB); 1985 | 1986 | Object turboRegR = doTurboReg(CsourcePathAndFileNameR, sourcePointsText); 1987 | Object turboRegG = doTurboReg(CsourcePathAndFileNameG, sourcePointsText); 1988 | Object turboRegB = doTurboReg(CsourcePathAndFileNameB, sourcePointsText); 1989 | 1990 | ImagePlus transformedSourceR = retrieveTransformedImage( turboRegR ); 1991 | ImagePlus transformedSourceG = retrieveTransformedImage( turboRegG ); 1992 | ImagePlus transformedSourceB = retrieveTransformedImage( turboRegB ); 1993 | 1994 | 1995 | ((ColorProcessor)imp.getProcessor()).setRGB( 1996 | (byte[])transformedSourceR.getProcessor().getPixels(), 1997 | (byte[])transformedSourceG.getProcessor().getPixels(), 1998 | (byte[])transformedSourceB.getProcessor().getPixels()); 1999 | break; 2000 | } 2001 | case ImagePlus.GRAY8: 2002 | case ImagePlus.GRAY16: 2003 | case ImagePlus.GRAY32: 2004 | { 2005 | //CsourcePathAndFileName = saveTempImageFile(source); 2006 | Object turboRegGR = doTurboReg(CsourcePathAndFileName, sourcePointsText); 2007 | 2008 | method = turboRegGR.getClass().getMethod("getTransformedImage", null); 2009 | ImagePlus transformedSource = (ImagePlus)method.invoke(turboRegGR, null); 2010 | transformedSource.getStack().deleteLastSlice(); 2011 | switch (imp.getType()) { 2012 | case ImagePlus.GRAY8: 2013 | { 2014 | transformedSource.getProcessor().setMinAndMax(0.0, 255.0); 2015 | final ImageConverter converter = new ImageConverter(transformedSource); 2016 | converter.convertToGray8(); 2017 | break; 2018 | } 2019 | case ImagePlus.GRAY16: 2020 | { 2021 | transformedSource.getProcessor().setMinAndMax(0.0, 65535.0); 2022 | final ImageConverter converter = new ImageConverter(transformedSource); 2023 | converter.convertToGray16(); 2024 | break; 2025 | } 2026 | case ImagePlus.GRAY32: 2027 | { 2028 | break; 2029 | } 2030 | default: 2031 | { 2032 | IJ.error("Unexpected image type"); 2033 | deleteTempFiles(); 2034 | return(null); 2035 | } 2036 | } 2037 | imp.setProcessor(null, transformedSource.getProcessor()); 2038 | break; 2039 | } 2040 | default: 2041 | { 2042 | IJ.error("Unexpected image type"); 2043 | deleteTempFiles(); 2044 | return(null); 2045 | } 2046 | } 2047 | } catch (NoSuchMethodException e) { 2048 | IJ.error("Unexpected NoSuchMethodException " + e); 2049 | return(null); 2050 | } catch (IllegalAccessException e) { 2051 | IJ.error("Unexpected IllegalAccessException " + e); 2052 | return(null); 2053 | } catch (InvocationTargetException e) { 2054 | IJ.error("Unexpected InvocationTargetException " + e); 2055 | return(null); 2056 | } catch (ClassNotFoundException e) { 2057 | IJ.error("Please download TurboReg_ from\nhttp://bigwww.epfl.ch/thevenaz/turboreg/"); 2058 | return(null); 2059 | } 2060 | 2061 | deleteTempFiles(); 2062 | return(source); 2063 | } /* end registerSlice */ 2064 | 2065 | /*------------------------------------------------------------------*/ 2066 | public ImagePlus[] createAdmissibleImageList ( 2067 | ) { 2068 | final int[] windowList = WindowManager.getIDList(); 2069 | final Stack stack = new Stack(); 2070 | for (int k = 0; ((windowList != null) && (k < windowList.length)); k++) { 2071 | final ImagePlus imp = WindowManager.getImage(windowList[k]); 2072 | if ((imp != null) && ((imp.getType() == imp.GRAY16) 2073 | || (imp.getType() == imp.GRAY32) 2074 | || ((imp.getType() == imp.GRAY8) && !imp.getStack().isHSB()))) { 2075 | stack.push(imp); 2076 | } 2077 | } 2078 | final ImagePlus[] admissibleImageList = new ImagePlus[stack.size()]; 2079 | int k = 0; 2080 | while (!stack.isEmpty()) { 2081 | admissibleImageList[k++] = (ImagePlus)stack.pop(); 2082 | } 2083 | if (k==0 && (windowList != null && windowList.length > 0 )){ 2084 | IJ.error("No grayscale images found! \n\nAre you using a color image?\n"+ 2085 | "If so, try splitting it into grayscale channels,\n"+ 2086 | "then use the best of those to align the stack\n"+ 2087 | "and apply the transformation to the rest."); 2088 | } 2089 | return(admissibleImageList); 2090 | } /* end createAdmissibleImageList */ 2091 | 2092 | public void setSrcImg(ImagePlus srcImg) { 2093 | this.srcImg = srcImg; 2094 | } 2095 | 2096 | public void setSrcAction(String srcAction) { 2097 | this.srcAction = srcAction; 2098 | } 2099 | 2100 | public void setTgtImg(ImagePlus tgtImg) { 2101 | this.tgtImg = tgtImg; 2102 | } 2103 | 2104 | public void setTgtAction(String tgtAction) { 2105 | this.tgtAction = tgtAction; 2106 | } 2107 | 2108 | /** 2109 | * Use field values for setting the translformation type. 2110 | */ 2111 | public void setTransformation(int transformation) { 2112 | this.transformation = transformation; 2113 | } 2114 | 2115 | public void setSaveTransform(boolean saveTransform) { 2116 | this.saveTransform = saveTransform; 2117 | } 2118 | 2119 | public void setViewManual(boolean viewManual) { 2120 | this.viewManual = viewManual; 2121 | } 2122 | 2123 | 2124 | 2125 | /** 2126 | * @param loadFile the loadFile to set 2127 | */ 2128 | public void setLoadFile(String loadFile) { 2129 | this.loadFile = loadFile; 2130 | } 2131 | 2132 | 2133 | 2134 | /** 2135 | * @param loadPath the loadPath to set 2136 | */ 2137 | public void setLoadPath(String loadPath) { 2138 | this.loadPath = loadPath; 2139 | } 2140 | 2141 | 2142 | 2143 | /** 2144 | * @param twoStackAlign the twoStackAlign to set 2145 | */ 2146 | public void setTwoStackAlign(boolean twoStackAlign) { 2147 | this.twoStackAlign = twoStackAlign; 2148 | } 2149 | 2150 | 2151 | 2152 | /** 2153 | * @param saveFile the saveFile to set 2154 | */ 2155 | public void setSaveFile(String saveFile) { 2156 | this.saveFile = saveFile; 2157 | } 2158 | 2159 | 2160 | 2161 | /** 2162 | * @param savePath the savePath to set 2163 | */ 2164 | public void setSavePath(String savePath) { 2165 | this.savePath = savePath; 2166 | } 2167 | 2168 | void deleteTempFiles(){ 2169 | if (CsourcePathAndFileNameR != ""){ 2170 | deleteAfile( CsourcePathAndFileNameR ); 2171 | } 2172 | if (CsourcePathAndFileNameG != ""){ 2173 | deleteAfile( CsourcePathAndFileNameG ); 2174 | } 2175 | if (CsourcePathAndFileNameB != ""){ 2176 | deleteAfile( CsourcePathAndFileNameB ); 2177 | } 2178 | if (CsourcePathAndFileName != ""){ 2179 | deleteAfile( CsourcePathAndFileName ); 2180 | } 2181 | if (CtargetPathAndFileName != ""){ 2182 | deleteAfile( CtargetPathAndFileName ); 2183 | } 2184 | 2185 | } 2186 | boolean deleteAfile(String filepath){ 2187 | //Files.deleteIfExists( filepath ); // > java1.7 2188 | File f = new File( filepath ); 2189 | if ( f.exists() ) { 2190 | try { 2191 | f.delete(); 2192 | } catch (SecurityException e){ 2193 | IJ.error("Temp file deleting failed, security exception" + e); 2194 | return false; 2195 | } 2196 | } 2197 | return true; 2198 | } 2199 | 2200 | public static void main(String[] args) { 2201 | MultiStackReg_ msr = new MultiStackReg_(); 2202 | ImagePlus srcimp = new ImagePlus("/Users/miura/Dropbox/sampleImages/RegistrationExample/translation/MAX_reference.tif"); 2203 | ImagePlus tgtimp = new ImagePlus("/Users/miura/Dropbox/sampleImages/RegistrationExample/translation/MAX_30-pixel-translation-along-x.tif"); 2204 | msr.setSrcImg(srcimp); 2205 | msr.setTgtImg(tgtimp); 2206 | msr.setSrcAction("Use as Reference"); 2207 | msr.setTgtAction("Align to First Stack"); 2208 | msr.setTransformation(0); 2209 | msr.setSaveTransform(true); 2210 | msr.core("", ""); 2211 | } 2212 | 2213 | } /* end class StackReg_ */ 2214 | 2215 | -------------------------------------------------------------------------------- /src/main/resources/plugins.config: -------------------------------------------------------------------------------- 1 | Plugins>Registration, "MultiStackReg", de.embl.cmci.registration.MultiStackReg_ 2 | Plugins>Registration, "Compress Matrices", de.embl.cmci.registration.Compress_Matrices 3 | 4 | --------------------------------------------------------------------------------