/*---------------------------------------------------------------------------------------------------------------------------------- Program created in the European Molecular Biology Laboratory in Heidelberg, Germany. Author: Kavagiou Zaharoula (Kavagiouzaharoula@yahoo.com) Supervisor / Group Leader: Dr Ernst Stelzer (stelzer@embl-heidelberg.de) ----------------------------------------------------------------------------------------------------------------------------------*/ /************************************************************************************************************ *Class patchwork_ includes methods that create a complete image from a mosaic of images that overlap. * *The program can handle 8-bit, 16-bit and color images. *1) The user should provide the number of the images he/she wants to combine, in the beginning. *2)Then, the user should open the images in the correct order (from the one in the right bottom to the one * on the left top, moving upwards. Imagine the mosaic as a matrix of images and start from element (n,m), * then (n-1,m)...,(n,m-1),(n-1,m-1)...to (0,0). The File Opener plugin is used (as found on the webpage) * in order to open the images. The user can also open the images before running the program and * choose when the File Opener runs. *3)The user should provide the number of the "rows" of the mosaic, the percentage of overlap vertically * and the percentage of overlap horizontically. *4)The option of eliminating background abnormalities is given. The images are divided by a "background * image" that is nothing but an image with no elements (of interest) in it (e.g. no cells). When the user * opens the background image, the program starts. * *Class patchwork_ uses classes: * ...correl2_ * ...correl3_ * ...Final_proc_ * ...File Opener_ ****************************************************************************************************************/ import ij.*; import ij.gui.*; import ij.process.*; import ij.plugin.PlugIn; import ij.io.FileSaver; import ij.plugin.filter.*; import ij.plugin.*; import ij.io.*; import java.io.*; import javax.swing.*; import javax.swing.filechooser.*; public class Patchwork_ implements PlugIn { /** im_num: total number of images*/ private int im_num=0; /** mcol: number of columns of images*/ private int mcol; /** mrow: number of rows*/ private int mrows; /** list of open images*/ private int[] wList; /** list of created images*/ private int[] cList; /** final image*/ public ImagePlus complete; /**background image*/ public ImagePlus back; public float overlap_ver, overlap_hor; private boolean error=false,backcor=false; public correl2_ cor2; public correl3_ cor3; public Final_proc_ div; public void run(String arg) { if (showdialog()) { if (mrows>1) create_column(); if (!error) create_row(); if (!error) { imshow(); new FileSaver( complete).saveAsTiff(); } WindowManager.closeAllWindows(); } } /** Method that implements the interface of the program */ public boolean showdialog() { GenericDialog gd1= new GenericDialog("Opening the images..."); // requests the images from the user gd1.addMessage( " Please open the images one after another.\n"+ " Start from the bottom right image of the mosaic\n"+ " and move towards the top one, as shown in \n"+ " the example of the 12-images mosaic with 3 columns: \n"+ " 12 8 4\n"+ " 11 7 3\n"+ " 10 6 2\n"+ " 9 5 1"); gd1.addNumericField(" Give the number of images of the mosaic :",0,0); gd1.showDialog(); if (gd1.wasCanceled()) return false; im_num=(int) gd1.getNextNumber(); IJ.runPlugIn("File_Opener"," "); wList=WindowManager.getIDList(); GenericDialog gd2 = new GenericDialog("Image Patchwork!"); // requests for the characteristics of the images gd2.addNumericField("Give the number of rows of the mosaic :",0,0); gd2.addNumericField("percentage (%) of overlap of columns: ",40,0); gd2.addNumericField("percentage (%) of overlap of rows: ",27,0); gd2.showDialog(); if (gd2.wasCanceled()) return false; mrows=(int)gd2.getNextNumber(); overlap_ver=(float) gd2.getNextNumber(); // the percentage of vertical ovelap overlap_ver=(float)overlap_ver/100; overlap_hor=(float) gd2.getNextNumber(); // the percentage of horizontal overlap overlap_hor=(float)overlap_hor/100; for (int i=0; i1) imp1=WindowManager.getImage(cList[0]); // mrow==1, when the user asks for a row of original images else imp1=WindowManager.getImage(wList[0]); for (int i=1;i1) imp2=WindowManager.getImage(cList[i]); else imp2=WindowManager.getImage(wList[i]); cor3.im_out(imp1,imp2,overlap_hor); if (cor3.fnum==0) { // matching could not be found message(cor3.mes); IJ.write("Correct the overlap of the rows!"); error=true; break; } ImageWindow w1; if (i!=1) w1=WindowManager.getCurrentWindow(); else w1=imp1.getWindow(); w1.close(); ImageWindow w2=imp2.getWindow(); w2.close(); imp1=cor3.impf; ImageProcessor ip=imp1.getProcessor(); new ImagePlus(" new row",ip).show(); } if (!error) complete=imp1; } /** Method that helps the user to correct wrong input. */ public void message(String mes) { if (mes== "smaller") IJ.write("wrong percentage of overlap.Try again with a smaller one"); else if (mes== "larger") IJ.write("wrong percentage of overlap.Try again with a larger one"); else IJ.write("wrong percentage of overlap.Try again with another one"); } /** Method that shows the final image */ public void imshow() { ImageProcessor c_ip; c_ip=complete.getProcessor(); new ImagePlus("Complete Image",c_ip).show(); } /******************************************************************************************************** * Correl2_ is a class with methods to create a column of vertically overlapping (aligned) images ********************************************************************************************************/ class correl2_ implements PlugIn { private float ratio; /** width and height of the 2 images that will be combined*/ private int width1,width2,height1,height2; private int num,excounter=0; /** percentage of overlap between the two images*/ private float overlap; private boolean doScaling = true,backcor; private ImageProcessor final_image; public ImagePlus impf; public String mes=" gap "; public int fnum,loop; public void run(String arg) { ImagePlus imp1=null; // the 2 images and the image we use to correct the background abnormalities ImagePlus imp2=null; ImagePlus back=null; im_out(imp1,imp2,overlap,backcor); } /** Method that returns the final image. * Im_out calls the basic methods to produce the combined image */ public void im_out (ImagePlus imp1, ImagePlus imp2, float overlap,boolean backcor) { this.overlap=overlap; fnum = corloops(imp1,imp2,backcor); if (fnum!=0) { imcreate(imp1,imp2); impf= new ImagePlus("final image",final_image); } } /** Method that finds the best match of two images. * Corloops calls the correlation method for two images, checks within a small range * of rows from the user's input and returns the row with the biggest * similarity level */ public int corloops(ImagePlus imp1, ImagePlus imp2,boolean backcor) { int ex1=0; // number of pixels of same value on the row of merging width1 = imp1.getWidth(); width2=imp2.getWidth(); height1 = imp1.getHeight(); height2 = imp2.getHeight(); ImageProcessor ip1=imp1.getProcessor(); ImageProcessor ip2=imp2.getProcessor(); ImageProcessor ip11,ip22; ip11=ip1.duplicate(); // making copy of ip1 to ip11 and convert ip11 to 8-bit grayscale IP ip11=ip11.convertToByte(doScaling); // 2 IPs of imp1; ip1 & ip11; ip1 changes imp1,ip11 doesn't if (!backcor) // implementing background correction on the images ip11=background(ip11,1,2); ip22=ip2.duplicate(); ip22=ip22.convertToByte(doScaling); if (!backcor) ip22=background(ip22,1,2); ip11.autoThreshold(); // produce binary images ip22.autoThreshold(); num=(int) (overlap*(float)height2); // number of rows overlapping float lowrange=0; if (overlap>=0.02) lowrange=overlap-(float)0.02; // range of overlap (lowrange to highrange) !!! u can adjust the percentage to your needs!!! float highrange=1; if (overlap<1) highrange=overlap+(float)0.02; int numlow= (int)(lowrange*(float)height2); // lower limit of comparison (in pixels) int numhigh=(int)(highrange*(float)height2); // upper limit of comparison float res=0,comp=0; // similarity levels loop=0; // final number of overlapping rows for (int i=numlow; i<=numhigh;i++) { res=correlate(ip11,ip22,i); if (res>comp) { // saving the best (biggest) level comp=res; loop=i; ex1=excounter; } } if (comp==0) loop=-1; if ((loop==numlow)||(loop==0)) { // we didn't find a turning point (maximum) or the ratio was small mes= "smaller"; // should search towards the direction that the level increases return (0); } else if (loop==numhigh) { if ((ex1==width1)&&(ex1==width2)) { //comparing identical images! mes="per_match"; return(loop); } mes="larger"; return (0); } else if ((loop==-1)||((float)ex1/width1<(float)0.65)) { // turning point was found but it is local maximum !!!adjust the percentage to your needs!!! mes="another"; return (0); } else { mes="match"; return (loop); } } /** Method that returns the percentage of similarity of 2 images. * Correlate compares parts of two images- (w,h1) & (w,h2)- * defined by the user's input of overlapping rows * and returns the level of their similarity */ public float correlate(ImageProcessor ip11, ImageProcessor ip22,int num) { int z1, z2, count=0; int y2= 0, x2=0, x1=0, y1=0,counter=0; int w1=width1; int h2=height2-num; // number of rows that don't overlap in imp2 int h1=num; // number of rows that overlap in imp1 for ( x1=0; x1=0.02) lowrange=overlap-(float)0.02; // range of overlap (lowrange to highrange) !!!adjust this percentage to your needs float highrange=1; if (overlap<1) highrange=overlap+(float)0.02; int numlow= (int)(lowrange*(float)width2); // lower limit of comparison (in pixels) int numhigh=(int)(highrange*(float)width2); // upper limit of comparison float res=0,comp=0; // similarity levels loop=0; // number of overlapping rows dif=Math.max(height1,height2)-Math.min(height1,height2); // difference of size between the 2 images for (int j=0;j<=dif;j++) { // loop that defines the shift of one image compared to the other ; the shift is defined by the difference |height1-height2| for (int i=numlow; i<=numhigh;i++) { // loop that checks for overlap from numlow column to numhigh res=correlate(ip11,ip22,i,j); if (res>comp) { // saving the best (biggest) level comp=res; loop=i; shift=j; ex1=excounter; } } if (comp==0) loop=-1; } if ((loop==numlow)||(loop==0)) { // we didn't find a turning point (maximum) or the ratio was small mes="smaller"; // should search towards the direction that the level increases return (0); } else if (loop==numhigh) { if ((height1==height2)&&(ex1==height1)) { //comparing identical images! mes="per_match"; return(loop); } mes="larger"; return (0); } else if ((loop==-1)||((float)ex1/minh<(float)0.65)) { // turning point was found but it's a local maximum !!!adjust this percentage to your needs!!! mes="another"; return (0); } else { mes="match"; return (loop); } } /** Method that returns the percentage of similarity of 2 images. * Correlate compares parts of two images- (w1,h1) & (w2,h2)- * defined by the user's input of overlapping columns * and returns the level of their similarity. The shift is taken into account. */ public float correlate(ImageProcessor ip11, ImageProcessor ip22,int num,int offset) { int z1, z2, count=0; int y2= 0, x2=0, x1=0, y1=0,counter=0; int offy1=0,offy2=0; // starting points of comparisons for y coordinate excounter=0; int w1=num; // number of columns that overlap in imp1 int w2=width2-num; // number of columns that don't overlap in imp2 int h2=height2; int h1=height1; minh=Math.min(h1,h2); // the smallest height of the two images if (minh==h1) offy2=offset; else offy1=offset; y2=offy2; for ( y1=offy1; y1=0;) { src = (int)(srcpixels[srcIndex++]&0xff); // src= image that we want to divide by the dst image if (src==0) dst = (float)256.0; else dst =(float)(210.0*( (float) (pixels[dstIndex]&0xff)) / ((float)src)); pixels[dstIndex++] = (byte)dst; } } /** Dividing 16-bit images */ public void divide16bit(ImageProcessor ip1,ImageProcessor ip2) { short[] srcpixels; short[] pixels; int src,dstIndex=0,srcIndex=0; float dst; int w= ip1.getWidth(); int h=ip1.getHeight(); pixels=(short[])ip1.getPixels(); srcpixels=(short[])ip2.getPixels(); for (int i=w*h;--i>=0;) { src = srcpixels[srcIndex++]&0xffff; if (src==0) dst = (float)65535.0; else dst =(float)(62000.0*( (float) (pixels[dstIndex]&0xffff)) / ((float)src)); pixels[dstIndex++] = (short)dst; } } /** Dividing color images */ public void dividecolor(ImageProcessor ip1,ImageProcessor ip2) { int[] srcpixels; int[] pixels; int pixr,pixg,pixb; int srcr,srcb,srcg,src,dstIndex=0,srcIndex=0; float dst,dstr,dstg,dstb; int w= ip1.getWidth(); int h=ip1.getHeight(); pixels=(int[])ip1.getPixels(); srcpixels=(int[])ip2.getPixels(); for (int i=w*h;--i>=0;) { srcr =(int) (srcpixels[srcIndex]&0xff0000)>>16; // seperate division for each color (red, green, blue) srcg =(int) (srcpixels[srcIndex]&0x00ff00)>>8; srcb =(int) (srcpixels[srcIndex]&0x0000ff); src=((srcr & 0xff) <<16) + ((srcg & 0xff) << 8) + (srcb & 0xff); if (src==0) { dstr = (float) 256.0; dstg = (float) 256.0; dstb = (float) 256.0; } else { pixr =(int) (pixels[srcIndex]&0xff0000)>>16; pixg =(int) (pixels[srcIndex]&0x00ff00)>>8; pixb=(int) (pixels[srcIndex]&0x0000ff); dstr =(float)(210.0*( (float) (pixr&0xff)) / ((float)srcr)); dstg =(float)(210.0*( (float) (pixg&0xff)) / ((float)srcg)); dstb =(float)(210.0*( (float) (pixb&0xff)) / ((float)srcb)); } dst=(float)((((int)dstr) & 0xff) <<16) + ((((int)dstg) & 0xff) << 8) + (((int)dstb) & 0xff); pixels[dstIndex++] = (int)dst; srcIndex++; } } } /** Uses the JFileChooser from Swing to open one or more images. */ class File_Opener implements PlugIn { private File dir; public void run(String arg) { openFiles(); IJ.register( File_Opener .class); } public void openFiles() { JFileChooser fc = null; try {fc = new JFileChooser();} catch (Throwable e) {IJ.error("This plugin requires Java 2 or Swing."); return;} fc.setMultiSelectionEnabled(true); if (dir==null) { String sdir = OpenDialog.getDefaultDirectory(); if (sdir!=null) dir = new File(sdir); } if (dir!=null) fc.setCurrentDirectory(dir); int returnVal = fc.showOpenDialog(IJ.getInstance()); if (returnVal!=JFileChooser.APPROVE_OPTION) return; File[] files = fc.getSelectedFiles(); if (files.length==0) { // getSelectedFiles does not work on some JVMs files = new File[1]; files[0] = fc.getSelectedFile(); } String path = fc.getCurrentDirectory().getPath()+Prefs.getFileSeparator(); dir = fc.getCurrentDirectory(); Opener opener = new Opener(); for (int i=0; i