package com.elphel.imagej.calibration; /** ** -----------------------------------------------------------------------------** ** PixelMapping.java ** ** Using camera calibration files to resample images for equirectangular projection ** ** ** Copyright (C) 2012 Elphel, Inc. ** ** -----------------------------------------------------------------------------** ** ** PixelMapping.java is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** -----------------------------------------------------------------------------** ** */ import java.awt.Point; import java.awt.Rectangle; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.SwingUtilities; import com.elphel.imagej.cameras.EyesisCorrectionParameters; import com.elphel.imagej.common.DoubleFHT; import com.elphel.imagej.common.DoubleGaussianBlur; import com.elphel.imagej.common.ShowDoubleFloatArrays; import com.elphel.imagej.jp4.JP46_Reader_camera; import com.elphel.imagej.readers.EyesisTiff; import Jama.Matrix; import ij.IJ; import ij.ImagePlus; import ij.ImageStack; import ij.Prefs; //import ij.gui.GenericDialog; import ij.io.FileSaver; import ij.io.Opener; import ij.process.ColorProcessor; import ij.text.TextWindow; import loci.common.services.DependencyException; import loci.common.services.ServiceException; import loci.formats.FormatException; public class PixelMapping { public SensorData [] sensors; public int panoWidth=0; public int panoHeight=0; public double panoDegreesPerPixel=Double.NaN; // needs to be initialized public double panoLongitudeLeft=Double.NaN; public double panoLongitudeRight=Double.NaN; public double panoLatitudeTop=Double.NaN; public double panoLatitudeBottom=Double.NaN; public int debugLevel=1; public int lanczosA=3; public int oversampled=2; public int binsPerHalfPixel=50; public double [][][][] lanczos=null; public int maxSensors=100; InterSensor lastUsedInterSensor=null; // public enum cellTypes {EMPTY_CELL, BAD_CELL, OLD_CELL,NEW_CELL} public PixelMapping (String defaultPath, int debugLevel){ this.debugLevel=debugLevel; String [] extensions={".calib-tiff"}; String [] defaultPaths=((defaultPath==null) || defaultPath.equals(""))?null:(new String[1]); if (defaultPaths!=null) defaultPaths[0]=defaultPath; MultipleExtensionsFileFilter parFilter = new MultipleExtensionsFileFilter("",extensions,"distortion calibration .calib-tiff files"); String [] calibFiles=CalibrationFileManagement.selectFiles(false, "Select camera sub-modules calibration files", "Select", parFilter, defaultPaths); // String [] defaultPaths); if ((calibFiles==null) || (calibFiles.length==0)) { IJ.showMessage("No files selected"); } int maxChannel=0; for (int i=0;imaxChannel) maxChannel=channel; } this.sensors=new SensorData[maxChannel+1]; for (int i=0;i 0)? num_channels: this.maxSensors]; for (int i=0;i=16) && (channel < 16)) { if (channel_file != channel) { System.out.println ("****Temporary fix to use old sensor files, COMMENT OUT when done debugging****"); System.out.println ("i="+i+", channel_file="+channel_file+", channel ="+channel+" -> "+channel_file); channel = channel_file; // i; } if ((channel < first_channel) || ((num_channels > 0) && (channel >= (first_channel + num_channels)))) { continue; // wrong channels } channel -= first_channel; if (update_channel) { sensorData.setChannel(channel); } this.sensors[channel]=sensorData; if (channel>maxChannel) maxChannel=channel; } SensorData [] tmpSensors=this.sensors.clone(); this.sensors=new SensorData[maxChannel+1]; for (int i=0;i=this.sensors.length)) return null; return this.sensors[channel].getPath(); } public int getSubChannel(int channel ){ if ((channel<0) || (channel>=this.sensors.length)) { System.out.println("ERROR: getSubChannel("+channel+"): channel out of range"); return -1; } // System.out.println("getSubChannel("+channel+") => "+this.sensors[channel].getSubChannel()); return this.sensors[channel].getSubChannel(); } public int getSubChannelSilent(int channel ){ if ((channel<0) || (channel>=this.sensors.length)) { return -1; } // System.out.println("getSubChannel("+channel+") => "+this.sensors[channel].getSubChannel()); return this.sensors[channel].getSubChannel(); } public int getSubCamera(int channel ){ if ((channel<0) || (channel>=this.sensors.length)) return -1; return this.sensors[channel].getSubCamera(); } // Enumerated calibration file public boolean isChannelAvailable(int channel){ return (this.sensors != null) && (channel>=0) && (channel= 0)) { return true; } return false; } // if subcameras are not used (-1), as in Quads, return {channel index} or null if alien file public int [] getSensorWH(int chn) { if ((sensors == null) || (chn < 0) || (chn >= sensors.length)) { return null; } return sensors[chn].getSensorWH(); } public int [] channelsForSubCamera(int subCamera){ if (subCamera < 0) { return null; } if (this.sensors == null) return null; if (this.debugLevel>1) { System.out.print("channelsForSubCamera("+subCamera+"),this.sensors.length="+this.sensors.length+": "); } if (!subcamerasUsed()) { for (int i=0;i cam_port = new ArrayList(); for (int i=0;i() { @Override public int compare(Point o1, Point o2) { return (o1.x>o2.x)? 1:((o1.x < o2.x)?-1:(o1.y > o2.y)? 1:((o1.y < o2.y)?-1:0)); } }); // debugging: if (subCamera >= cam_port_arr.length) { if (this.debugLevel>1) { System.out.println("Error: Subcamera "+subCamera+" > that total number of sensor ports in the system = "+cam_port_arr.length); } return null; } if (this.debugLevel>1) { System.out.println("----- This filename subcamera "+subCamera+": physical camera "+cam_port_arr[subCamera].x+", sensor_port "+cam_port_arr[subCamera].y); } else if (this.debugLevel > -2) { System.out.print("."); } int numChannels=0; for (int i=0;i=0) && (channel=this.sensors.length))return null; return this.sensors[channel].getBayerFlatFieldFloat( bayer, range); } public double [] getBayerFlatField( int channel, int [][] bayer){ //{{1,0},{2,1}} GR/BG if ((this.sensors == null) || (channel<0) && (channel>=this.sensors.length))return null; return this.sensors[channel].getBayerFlatField( // width, // height, bayer); } public int [][] getDefectsXY( int channel){ if ((this.sensors == null) || (channel<0) && (channel>=this.sensors.length))return null; return this.sensors[channel].getDefectsXY(); } public double[] getDefectsDiff( int channel){ if ((this.sensors == null) || (channel<0) && (channel>=this.sensors.length))return null; return this.sensors[channel].getDefectsDiff(); } ///SensorData /* public float [] getBayerFlatFieldFloat( int width, int height, int [][] bayer){ //{{1,0},{2,1}} GR/BG * int maxChannel=0; for (int i=0;imaxChannel) maxChannel=channel; } this.sensors=new SensorData[maxChannel+1]; for (int i=0;imaxChannel) maxChannel=channel; } if (this.sensors==null) { this.sensors=new SensorData[maxChannel+1]; for (int i=0;i0) System.out.println(msg); if (this.sensors[channel]==null) this.sensors[channel] =new SensorData(channelPath,true); else this.sensors[channel].createEquirectangularMap(channelPath); } } public void loadChannelEquirectangularMap( int channel, String path){ if ((this.sensors==null) || (this.sensors[channel]==null)){ String msg="Sensor "+channel+" data is not initialized"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } // this.sensors[channel] =new SensorData(path,true); this.sensors[channel].createEquirectangularMap(path); } public void resampleToEquirectangular( String [] paths, int sourceImageScale, boolean showResults, boolean saveResults, int maxThreads){ for (int i=0;ichannel) && (this.sensors[channel]!=null)){ this.sensors[channel].equirectangularMap=null; } } public void deleteEquirectangularMapFull(int channel){ if ((this.sensors!=null) && (this.sensors.length>channel) && (this.sensors[channel]!=null) && (this.sensors[channel].equirectangularMap!=null)){ this.sensors[channel].equirectangularMap.map=null; } } public boolean isEquirectangularMapAvailable(int channel){ if ((this.sensors!=null) && (this.sensors.length>channel) && (this.sensors[channel]!=null)){ return((this.sensors[channel].equirectangularMap!=null) && (this.sensors[channel].equirectangularMap.partialMap!=null)); } return false; } public boolean isPlaneMapMapAvailable(int channel){ if ((this.sensors!=null) && (this.sensors.length>channel) && (this.sensors[channel]!=null)){ return((this.sensors[channel].interSensor!=null)); } return false; } public ImagePlus resampleToEquirectangular( ImagePlus imp, int channel, int sourceImageScale, int maxThreads){ if (imp==null) return null; if ((this.sensors==null) || (this.sensors.length<(channel+1) || (this.sensors[channel]==null))){ IJ.showMessage("No sensor data for channel "+channel); return null; } if (( this.sensors[channel].equirectangularMap==null) || ( this.sensors[channel].equirectangularMap.partialMap==null)){ IJ.showMessage("No equirectangular map for channel "+channel); return null; } if (this.lanczos==null) generateLanczosStack(); if (imp.getType()==ImagePlus.COLOR_RGB) { return resampleToEquirectangularRGB24(imp, channel, sourceImageScale, maxThreads); } else if (imp.getStackSize()>=3) { return resampleToEquirectangularRGBFP32(imp, channel, sourceImageScale, maxThreads); } IJ.showMessage("Not yet implemented for this image type"); return null; } /** * Warp 3-slice 32-bit FP stack (RGB) to 4-slice stack RGBA * @param imp 3-slice image stack with straight image * @param channel sensor channel number * @param scale source image scale (normally 2x) * @param maxThreads * @return 4-slice RGBA image, floating point 32 bits per sample per color channel */ public ImagePlus resampleToEquirectangularRGBFP32( ImagePlus imp, int channel, final int scale, int maxThreads){ final SensorData.EquirectangularMap erm=this.sensors[channel].equirectangularMap; final int width=imp.getWidth(); final int height=imp.getHeight(); if (scale==0) { IJ.showMessage("resampleToEquirectangularRGBFP32(): Image has less resolution than the map, can not proceed"); return null; } if (imp.getStackSize()<3) { IJ.showMessage("resampleToEquirectangularRGBFP32(): Expecting 3-layer stack(R,G,B)"); return null; } final float [][] imagePixels= new float [3][]; for (int c=0;c0){ // do not convolve pixels with alpha=0; double x=scale*erm.partialMap[0][oIndex]; double y=scale*erm.partialMap[1][oIndex]; int ix= (int) Math.round(x); int iy= (int) Math.round(y); double dx=x-ix; double dy=y-iy; int indxX= (int) Math.round((2*dx+1)*binsPerHalfPixel); int indxY= (int) Math.round((2*dy+1)*binsPerHalfPixel); double [][] lk=lanczos[indxY][indxX]; for (int i=0;i=height) ipy=height-1; int baseI=ipy*width; for (int j=0;j=width) ipx=width-1; /* int pix=imagePixels[baseI+ipx]; RGBA[1]+=((pix>>16) & 0xff)*lk[i][j]; // R RGBA[2]+=((pix>>8) & 0xff)*lk[i][j]; // G RGBA[3]+=( pix & 0xff)*lk[i][j]; // B */ if ((baseI+ipx)>imagePixels[0].length){ System.out.println("baseI+ipx="+(baseI+ipx)+" baseI="+baseI+" ipx="+ipx+" i="+i+" j="+j+ " erm.mapWOI.width="+erm.mapWOI.width+" erm.mapWOI.height="+erm.mapWOI.height); } RGBA[1]+=imagePixels[0][baseI+ipx]*lk[i][j]; // R RGBA[2]+=imagePixels[1][baseI+ipx]*lk[i][j]; // G RGBA[3]+=imagePixels[2][baseI+ipx]*lk[i][j]; // B } } outPixels[0][oIndex]=(float) RGBA[1]; outPixels[1][oIndex]=(float) RGBA[2]; outPixels[2][oIndex]=(float) RGBA[3]; /* int c=0; for (int i=0;i<4;i++){ if (RGBA[i]< 0.0) RGBA[i]=0.0; else if (RGBA[i] > 255.0) RGBA[i]=255.0; c=(c<<8) | ((int) RGBA[i]); outPixels [oIndex]=c; } */ } else { outPixels[0][oIndex]=0.0F; outPixels[1][oIndex]=0.0F; outPixels[2][oIndex]=0.0F; // outPixels[oIndex]=0; } } final int numFinished=opyDoneAtomic.getAndIncrement(); // IJ.showProgress(progressValues[numFinished]); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // Here, we can safely update the GUI // because we'll be called from the // event dispatch thread IJ.showProgress(numFinished,erm.mapWOI.height); } }); } } }; } startAndJoin(threads); ImageStack outStack=new ImageStack(erm.mapWOI.width,erm.mapWOI.height); outStack.addSlice("Red", outPixels[0]); outStack.addSlice("Green", outPixels[1]); outStack.addSlice("Blue", outPixels[2]); outStack.addSlice("Alpha", outPixels[3]); // ColorProcessor cp=new ColorProcessor(erm.mapWOI.width,erm.mapWOI.height); // cp.setPixels(outPixels); ImagePlus impOut=new ImagePlus(imp.getTitle()+"_EQR",outStack); impOut.setProperty("channel", ""+erm.channel); // image channel impOut.setProperty("XPosition", ""+erm.mapWOI.x); // real XPosition as tiff tag 0x11e is rational (5) impOut.setProperty("YPosition", ""+erm.mapWOI.y); // real XPosition as tiff tag 0x11f is rational (5) impOut.setProperty("ImageFullWidth", "" +erm.pixelsHorizontal);// real ImageFullWidth as tiff tag 0x8214 is rational (5) impOut.setProperty("ImageFullLength", ""+erm.pixelsVertical); // real ImageFullLength as tiff tag 0x8215 is rational (5) (new JP46_Reader_camera(false)).encodeProperiesToInfo(impOut); impOut.getProcessor().resetMinAndMax(); return impOut; } public ImagePlus resampleToEquirectangularRGB24( ImagePlus imp, int channel, final int scale, int maxThreads){ final SensorData.EquirectangularMap erm=this.sensors[channel].equirectangularMap; final int width=imp.getWidth(); final int height=imp.getHeight(); if (scale==0) { IJ.showMessage("Image has less resolution than the map, can not proceed"); return null; } final int [] imagePixels=(int []) imp.getProcessor().getPixels(); final int [] outPixels=new int [erm.mapWOI.width*erm.mapWOI.height]; if (this.oversampled!=scale) generateLanczosStack( this.lanczosA, scale, this.binsPerHalfPixel); final double [][][][] lanczos=this.lanczos; final int center=this.lanczos[0][0][0].length/2; final Thread[] threads = newThreadArray(maxThreads); final AtomicInteger opyAtomic = new AtomicInteger(0); final AtomicInteger opyDoneAtomic = new AtomicInteger(1); final int binsPerHalfPixel=this.binsPerHalfPixel; // for (int opy=0;opy0){ // do not convolve pixels with alpha=0; double x=scale*erm.partialMap[0][oIndex]; double y=scale*erm.partialMap[1][oIndex]; int ix= (int) Math.round(x); int iy= (int) Math.round(y); double dx=x-ix; double dy=y-iy; int indxX= (int) Math.round((2*dx+1)*binsPerHalfPixel); int indxY= (int) Math.round((2*dy+1)*binsPerHalfPixel); double [][] lk=lanczos[indxY][indxX]; for (int i=0;i=height) ipy=height-1; int baseI=ipy*width; for (int j=0;j=width) ipx=width-1; int pix=imagePixels[baseI+ipx]; RGBA[1]+=((pix>>16) & 0xff)*lk[i][j]; // R RGBA[2]+=((pix>>8) & 0xff)*lk[i][j]; // G RGBA[3]+=( pix & 0xff)*lk[i][j]; // B } } int c=0; for (int i=0;i<4;i++){ if (RGBA[i]< 0.0) RGBA[i]=0.0; else if (RGBA[i] > 255.0) RGBA[i]=255.0; c=(c<<8) | ((int) RGBA[i]); outPixels [oIndex]=c; } } else { outPixels[oIndex]=0; } } final int numFinished=opyDoneAtomic.getAndIncrement(); // IJ.showProgress(progressValues[numFinished]); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // Here, we can safely update the GUI // because we'll be called from the // event dispatch thread IJ.showProgress(numFinished,erm.mapWOI.height); } }); } } }; } startAndJoin(threads); ColorProcessor cp=new ColorProcessor(erm.mapWOI.width,erm.mapWOI.height); cp.setPixels(outPixels); ImagePlus impOut=new ImagePlus(imp.getTitle()+"_EQR",cp); impOut.setProperty("channel", ""+erm.channel); // image channel impOut.setProperty("XPosition", ""+erm.mapWOI.x); // real XPosition as tiff tag 0x11e is rational (5) impOut.setProperty("YPosition", ""+erm.mapWOI.y); // real XPosition as tiff tag 0x11f is rational (5) impOut.setProperty("ImageFullWidth", "" +erm.pixelsHorizontal);// real ImageFullWidth as tiff tag 0x8214 is rational (5) impOut.setProperty("ImageFullLength", ""+erm.pixelsVertical); // real ImageFullLength as tiff tag 0x8215 is rational (5) (new JP46_Reader_camera(false)).encodeProperiesToInfo(impOut); impOut.getProcessor().resetMinAndMax(); return impOut; } /** * Generate a stack of Lanczos kernels to for re-sampling * @param lacrososA - value of "a" in sinc(x)*sinc(x/a) * @param oversampled reduce resolution of the image if it was over-sampled (2 for current Eeyesis software) * @param binsPerHalfPixel calculate for this number of fractional pixels (oversampled pixels, not sensor ones) * @return [fractional pixels vertical] [fractional pixels horizontal][delta pixel horizontal][ delta pixel vertical] */ public double [][][][] generateLanczosStack(){ return generateLanczosStack( this.lanczosA, this.oversampled, this.binsPerHalfPixel); } public double [][][][] generateLanczosStack( int lanczosA, int oversampled, int binsPerHalfPixel){ this.lanczosA=lanczosA; this.oversampled=oversampled; this.binsPerHalfPixel=binsPerHalfPixel; if (this.debugLevel>0) System.out.println("Generating Lanczos kernel stack with A="+ this.lanczosA+", oversampled="+oversampled+", binsPerHalfPixel="+binsPerHalfPixel); double [][][][] lanczos= new double [2*binsPerHalfPixel+1][2*binsPerHalfPixel+1][2*lanczosA*oversampled+1][2*lanczosA*oversampled+1]; double step=0.5/binsPerHalfPixel; double centerPix=lanczosA*oversampled; double pi2=Math.PI*Math.PI; double s=lanczosA/pi2/oversampled; double sync0=1.0/oversampled; for (int iy=0;iy=lanczosA)?0.0:((py==0.0)?sync0:(s*Math.sin(py*Math.PI)*Math.sin(py*Math.PI/lanczosA)/(py*py))); for (int ipx=0;ipx=lanczosA)?0.0:((px==0.0)?sync0:(s*Math.sin(px*Math.PI)*Math.sin(px*Math.PI/lanczosA)/(px*px)))); } } } } } this.lanczos=lanczos; if (this.debugLevel>0) System.out.println("Generated Lanczos kernel stack with A="+ this.lanczosA+", oversampled="+oversampled+", binsPerHalfPixel="+binsPerHalfPixel); return lanczos; } public double [] testLanczosCenter(double [][][][] lanczos){ int size= lanczos.length; int center= lanczos[0][0].length/2; double [] testArr=new double [size*size]; for (int i=0;i0) System.out.println("Accumulating channel "+channelNumber); SensorData.EquirectangularMap erm=this.sensors[channelNumber].equirectangularMap; if (pixels==null){ this.panoWidth=erm.pixelsHorizontal; this.panoHeight=erm.pixelsVertical; pixels= new float [this.panoWidth*this.panoHeight]; for (int i=0;i0){ // alpha pixels[(iTileLat+woi.y)*this.panoWidth+((iTileLong+woi.x) % this.panoWidth)]+=erm.partialMap[2][tileIndex-1]; } } } } return pixels; } /** * For up to 32 sensors, calculates in which of them current lat/long is available * @param threshold - count only pixels with alpha > this threshold (0.0 - all non-zero) * @return bit masks in scan-line (long, then lat) order * sets this.panoDegreesPerPixel */ public int [] generateOverlapBits( double threshold, int [] sensorList ){ if (sensorList==null){ // all sensors sensorList=new int [this.sensors.length]; for (int i=0;i0) System.out.println("Accumulating channel "+channelNumber); SensorData.EquirectangularMap erm=this.sensors[channelNumber].equirectangularMap; if (usedSensors==null){ this.panoWidth=erm.pixelsHorizontal; this.panoHeight=erm.pixelsVertical; usedSensors= new int [this.panoWidth*this.panoHeight]; for (int i=0;ithreshold){ // alpha usedSensors[(iTileLat+woi.y)*this.panoWidth+((iTileLong+woi.x) % this.panoWidth)] |= (1<=overlapThreshold) numPairs++; } int [][] pairs = new int [numPairs][2]; numPairs=0; for (int i=0;i<(overlap.length-1);i++) for (int j=i+1;j=overlapThreshold) { pairs[numPairs ][0]=i; pairs[numPairs++][1]=j; } } return pairs; } /* double threshold=0.01*gd.getNextNumber(); double [][] overlap=PIXEL_MAPPING.overlapPairsAreas(threshold); PIXEL_MAPPING.listOverlap(overlap); for (int i=0;i<(overlap.length-1);i++) for (int j=i+1;j=0) indexSuffix--; // remove 1 or 2 digits before period for (int channelNumber=0;channelNumber0) System.out.println(msg); sensors[channelNumber].combineDistortionsEquirectangularToSensor( channelNumber, longitudeLeft, longitudeRight, latitudeTop, latitudeBottom, pixelsHorizontal, imageWidth, //int width, imageHeight, //int height, x0, //double x0, y0, //double y0, pixelStep, //double pixelStep, maxThreads); sensors[channelNumber].equirectangularMap.createPartialMap(longitudeCrop); if (deleteFull) sensors[channelNumber].equirectangularMap.map=null; String channelPath=path.substring(0,indexSuffix)+String.format("%02d",channelNumber)+path.substring(indexPeriod); msg="Saving equirectangular map to "+channelPath; IJ.showStatus(msg); if (this.debugLevel>0) System.out.println(msg); sensors[channelNumber].equirectangularMap.saveMapAsImageStack("EQRCT_MAP"+channelNumber, channelPath); if (deleteAll) sensors[channelNumber].equirectangularMap=null; if (this.debugLevel >1) System.out.println("Saved equirectangular map "+channelNumber+" at "+ IJ.d2s(0.000000001*(System.nanoTime()-startTime),3)); } if (this.debugLevel >0) System.out.println("Finished equirectangular maps at "+ IJ.d2s(0.000000001*(System.nanoTime()-startTime),3)); IJ.showStatus("Finished equirectangular maps"); } public int getNumSubCameras(){ return (this.sensors==null)?0:this.sensors.length; } public double [] getParametersVector(int chn){ return this.sensors[chn].getParametersVector(); } public String getParameterName(int index){ return (new SensorData()).getParameterName(index); } public String getParameterDescription(int index){ return (new SensorData()).getParameterDescription(index); } public String getParameterUnits(int index){ return (new SensorData()).getParameterUnits(index); } public double[][] applyOverlapMap( String path, // String imgPathFormat, String resultDirectory, int sourceImageScale, // 2.0 boolean saveTiff, boolean convertToDouble, int maxThreads, int debugLevel ){ Opener opener=new Opener(); ImagePlus impMap=opener.openImage("", path); if (impMap==null) { String msg="Failed to read inter-sensor overlap map file "+path; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (debugLevel>0) System.out.println("Read "+path+" as an inter-sensor overlap map"); (new JP46_Reader_camera(false)).decodeProperiesFromInfo(impMap); InterSensor interSensor=new InterSensor(impMap,debugLevel); this.lastUsedInterSensor=interSensor; double [][] aYCbCr=convertToDouble?(new double [8][]):null; for (int iChn=0;iChn<2;iChn++){ int channel=interSensor.channel[iChn]; String sourcePath=String.format(imgPathFormat,channel); ImagePlus impSrc=opener.openImage("", sourcePath); if (impSrc==null) { String msg="Failed to read sensor image file "+sourcePath; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (debugLevel>0) System.out.println("Read "+sourcePath+" as a sensor image"); if (this.lanczos==null) generateLanczosStack(); if (this.oversampled!=sourceImageScale) generateLanczosStack( this.lanczosA, sourceImageScale, this.binsPerHalfPixel); if (impSrc.getType()!=ImagePlus.COLOR_RGB) { String msg="Not yet implemented for this image type, only RGB24 is supported"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } ImagePlus impOverlap=interSensor.resampleToOverlapRGB24( true, // it is overlap of a pair of images impSrc, channel, sourceImageScale, this.lanczos, this.binsPerHalfPixel, maxThreads); if (impOverlap==null){ String msg="Failed to apply overlap map to "+sourcePath; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (debugLevel>1){ impOverlap.getProcessor().resetMinAndMax(); // imp_psf will be reused impOverlap.show(); } //resultDirectory if (saveTiff){ String outPath=((resultDirectory.length()<1)?"":(resultDirectory+Prefs.getFileSeparator()))+impOverlap.getTitle()+".tiff"; try { (new EyesisTiff()).saveTiff( impOverlap, outPath, 3, // float32 255.0, true, debugLevel); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ServiceException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (DependencyException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (convertToDouble) interSensor.argb24ToAYCbCr( impOverlap, iChn, // 0 - first image, 1 - second image in the pair aYCbCr); } if (convertToDouble) interSensor.overlapImages=aYCbCr; return aYCbCr; } public InterSensor loadPlaneMap( String path, // int debugLevel ){ Opener opener=new Opener(); ImagePlus impMap=opener.openImage("", path); if (impMap==null) { String msg="Failed to read plane-to-sensor map file "+path; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (debugLevel>0) System.out.println("Read "+path+" as a plane-to-sensor map"); (new JP46_Reader_camera(false)).decodeProperiesFromInfo(impMap); InterSensor interSensor=new InterSensor(impMap,debugLevel); this.lastUsedInterSensor=interSensor; for (int iChn=0;iChn1) System.out.println("loadPlaneMap():this.sensors["+channel+"]==null"); } else { this.sensors[channel].interSensor=interSensor; // OK if some sensors are not used for the selected source files } } return interSensor; } public void applyPlaneMap( String path, // String imgPathFormat, String resultDirectory, int sourceImageScale, // 2.0 boolean saveTiff, int maxThreads, int debugLevel ){ InterSensor interSensor=loadPlaneMap( path, // debugLevel ); Opener opener=new Opener(); for (int iChn=0;iChn0) System.out.println("Failed to read sensor image file "+sourcePath); continue; } if (debugLevel>0) System.out.println("Read "+sourcePath+" as a sensor image"); if (this.lanczos==null) generateLanczosStack(); if (this.oversampled!=sourceImageScale) generateLanczosStack( this.lanczosA, sourceImageScale, this.binsPerHalfPixel); if (impSrc.getType()!=ImagePlus.COLOR_RGB) { String msg="Not yet implemented for this image type, only RGB24 is supported"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } ImagePlus impOverlap=interSensor.resampleToOverlapRGB24( false, // it is plane, not overlap of a pair of images impSrc, channel, sourceImageScale, this.lanczos, this.binsPerHalfPixel, maxThreads); if (impOverlap==null){ String msg="Failed to apply plane map to "+sourcePath; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (debugLevel>1){ impOverlap.getProcessor().resetMinAndMax(); // imp_psf will be reused impOverlap.show(); } //resultDirectory if (saveTiff){ String outPath=((resultDirectory.length()<1)?"":(resultDirectory+Prefs.getFileSeparator()))+impOverlap.getTitle()+".tiff"; try { (new EyesisTiff()).saveTiff( impOverlap, outPath, 3, // float32 255.0, true, debugLevel); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ServiceException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (DependencyException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public ImagePlus applyPlaneMap( ImagePlus impSrc, int channel, int sourceImageScale, // 2.0 int maxThreads, int debugLevel ){ InterSensor interSensor=this.sensors[channel].interSensor; if (this.lanczos==null) generateLanczosStack(); if (this.oversampled!=sourceImageScale) generateLanczosStack( this.lanczosA, sourceImageScale, this.binsPerHalfPixel); if (impSrc.getType()!=ImagePlus.COLOR_RGB) { String msg="Not yet implemented for this image type, only RGB24 is supported"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } ImagePlus impPlane=interSensor.resampleToOverlapRGB24( false, // it is plane, not overlap of a pair of images impSrc, channel, sourceImageScale, this.lanczos, this.binsPerHalfPixel, maxThreads); if (impPlane==null){ String msg="Failed to apply plane map to channel "+channel; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (debugLevel>2){ impPlane.getProcessor().resetMinAndMax(); // imp_psf will be reused impPlane.show(); } return impPlane; } public void generateAndSaveInterSensorMaps( String directory, String fileNameFormat, //prefix%02d-%02dsuffix double alphaThreshold, double overlapThreshold, double angleSizeX, double angleSizeY, int overlapExtraPixels, boolean updateStatus, int debugLevel ){ int [][] pairs =findSensorPairs( alphaThreshold, overlapThreshold); if (debugLevel>0) { System.out.println("Detected "+pairs.length+" overlapping sensor pairs"); if (debugLevel>1){ for (int numPair=0;numPair1) System.out.println("Processing sensor pair "+(numPair+1)+ " ( of "+pairs.length+") - sensor "+ pairs[numPair][0]+" and sensor "+pairs[numPair][1]); String title=String.format(fileNameFormat,pairs[numPair][0],pairs[numPair][1]); ImagePlus imp=interSensorOverlapMap( pairs[numPair][0], pairs[numPair][1], angleSizeX, angleSizeY, overlapExtraPixels, title, debugLevel); if (imp!=null) { if (debugLevel>2) { imp.getProcessor().resetMinAndMax(); // imp_psf will be reused imp.show(); } String path=((directory!=null) && (directory.length()>0)?(directory+Prefs.getFileSeparator()):"")+title; FileSaver fs=new FileSaver(imp); String msg="Saving ovelap map for sensors "+pairs[numPair][0]+" and "+pairs[numPair][1]+": "+path; if (updateStatus) IJ.showStatus(msg); if (debugLevel>0) System.out.println(msg); fs.saveAsTiffStack(path); } else { // imp==null if (debugLevel>0){ System.out.println("No overlap detected for sensors "+pairs[numPair][0]+" and "+pairs[numPair][1]); } } } } /** * Create 3xNumber of sensors (channels) map from plane pixels to individual sensor images pixels (pixel-X, pixel-Y, alpha(mask) ) * Uses intermediate equirectangular map that should be defined * @param channels - array of sensor numbers * @param projectionElevation - Latitude (in degrees) of the normal to the projection plane * @param projectionYaw - Longitude (in degrees) of the normal to the projection plane * @param projectionRoll - Rotation of the projection plane around the perpendicular from the lens centers * @param projectionPixelSize ratio of the plane pixel size to the distance from the lens center to the projection plane * @param projectionWidth - width of the projection rectangle * @param projectionHeight - height of the projection rectangle * @param projectionCenterX - X-coordinate (along the projection plane X - right) of the intersection of the projection plane with the perpendicular from the lens center * @param projectionCenterY - Y-coordinate (along the projection plane Y - down) of the intersection of the projection plane with the perpendicular from the lens center * @param title - Image title * @param debugLevel - debug level * @return image with 3*N slices - {pixelX1, pixelY1, alpha1, pixelX2, pixelY2, alpha2, ...} and metadata needed to map images */ // TODO: Make NaN for projectionPixelSize,projectionCenterX,projectionCenterY and 0 for projectionWidth - then find automatically // also save as parameters public ImagePlus getPlaneToSensorsMap( int [] channels, double projectionElevation, double projectionYaw, double projectionRoll, double projectionPixelSize, int projectionWidth, int projectionHeight, double projectionCenterX, double projectionCenterY, double nominalHorizontalDisparity, // nominal distance between horizontal cameras String title, int debugLevel ){ generateOverlapBits(0.1, channels ); // sets this.panoDegreesPerPixel among other things // determine projectionPixelSize (should be the same for all sensors if (Double.isNaN(projectionPixelSize) || (projectionPixelSize<=0.0)) { projectionPixelSize=Math.PI/180.0*this.panoDegreesPerPixel; if (debugLevel>0) System.out.println("getPlaneToSensorsMap(): Using projectionPixelSize="+projectionPixelSize); // SensorData.EquirectangularMap erm=this.sensors[channels[0]].equirectangularMap; // projectionPixelSize=erm.degreesPerPixel*Math.PI/180; } // create lat/long pair for each pixel of the projection plane double projPsi=Math.PI/180*projectionRoll; double projTheta=Math.PI/180*projectionElevation; double projPhi=Math.PI/180*projectionYaw; // rotate by - projPsi around projection plane z (normal) double [][] aR10={ { Math.cos(projPsi), Math.sin(projPsi), 0.0}, {-Math.sin(projPsi), Math.cos(projPsi), 0.0}, { 0.0, 0.0, 1.0}}; Matrix R10=new Matrix(aR10,3,3); // rotate by - projTheta around projection plane X double [][] aR21={ {1.0, 0.0, 0.0 }, {0.0, Math.cos(projTheta), Math.sin(projTheta)}, {0.0,-Math.sin(projTheta), Math.cos(projTheta)}}; Matrix R21=new Matrix(aR21,3,3); // rotate by -projPhi around projection plane Y double [][] aR32={ { Math.cos(projPhi), 0.0, Math.sin(projPhi)}, { 0.0, 1.0, 0.0 }, {-Math.sin(projPhi), 0.0, Math.cos(projPhi)}}; Matrix MA=((new Matrix(aR32,3,3)).times(R21)).times(R10); float [][][] projLatLong = new float [projectionHeight][projectionWidth][2]; double [][] V={{0.0},{0.0},{1.0}}; Matrix MV=new Matrix(V,3,1); for (int py=0;py2) { int i0=0; double [][] dbgVect=new double [3][projectionHeight*projectionWidth]; for (int py=0;py channel2 vector * @param channels array of channels (sensors) to use * @param channel1 right sub-camera * @param channel2 left sub-camera * @param debugLevel debug level * @param strictHorizontalDisparity - set roll to make two cameras have pure horizontal disparity (false - minimize overall roll) * @return (Yaw, elevation, roll) of the projection plane in degrees */ public double [] suggestProjectionPlaneYawElevationRoll( int [] channels, int channel1, int channel2, boolean strictHorizontalDisparity, int debugLevel ){ int debugThreshold=1; double [][] VZ={{0.0},{0.0},{1.0}}; double [][] VX={{1.0},{0.0},{0.0}}; double [][] VY={{0.0},{1.0},{0.0}}; // up? double [] averageViewVector={0.0,0.0,0.0}; double [] averageViewXVector={0.0,0.0,0.0}; for (int iSens=0;iSens=debugThreshold) { System.out.println("iSens="+iSens+", chn="+chn); System.out.println("this.sensors["+chn+"].theta"+this.sensors[chn].theta+", theta="+theta); System.out.println("this.sensors["+chn+"].azimuth"+this.sensors[chn].azimuth+ " this.sensors["+chn+"].phi"+this.sensors[chn].phi+" (sum="+ (this.sensors[chn].azimuth+this.sensors[chn].phi)+ ", phi="+phi); } // rotate by - projPsi around projection plane z (normal) double [][] aR10={ { Math.cos(psi), Math.sin(psi), 0.0}, {-Math.sin(psi), Math.cos(psi), 0.0}, { 0.0, 0.0, 1.0}}; Matrix R10=new Matrix(aR10,3,3); // rotate by - projTheta around projection plane X double [][] aR21={ {1.0, 0.0, 0.0 }, {0.0, Math.cos(theta), Math.sin(theta)}, {0.0,-Math.sin(theta), Math.cos(theta)}}; Matrix R21=new Matrix(aR21,3,3); // rotate by -projPhi around projection plane Y double [][] aR32={ { Math.cos(phi), 0.0, Math.sin(phi)}, { 0.0, 1.0, 0.0 }, {-Math.sin(phi), 0.0, Math.cos(phi)}}; Matrix MA=(new Matrix(aR32,3,3)).times(R21.times(R10)); if (debugLevel>=debugThreshold) { System.out.println("R21="); R21.print(10,5); System.out.println("R32="); (new Matrix(aR32,3,3)).print(10,5); System.out.println("MA="); MA.print(10,5); } double [] sensorViwewVector= unityVector((MA.times(new Matrix(VZ))).getRowPackedCopy()); double [] sensorViwewXVector=unityVector((MA.times(new Matrix(VX))).getRowPackedCopy()); for (int i=0;i=debugThreshold) { System.out.println("Sensor view vector=("+sensorViwewVector[0]+","+sensorViwewVector[1]+","+sensorViwewVector[2]+")"); System.out.println("Sensor view X vector=("+sensorViwewXVector[0]+","+sensorViwewXVector[1]+","+sensorViwewXVector[2]+")"); } averageViewVector[i]+= sensorViwewVector[i]; averageViewXVector[i]+=sensorViwewXVector[i]; } } double [] planeZ=unityVector(averageViewVector); double [] planeX=unityVector(crossProduct3(planeZ,crossProduct3(averageViewXVector,planeZ))); double [] planeXM={-planeX[0],-planeX[1],-planeX[2]}; if (debugLevel>=debugThreshold) { System.out.println("suggestProjectionPlaneYawElevationRoll(channels[],"+channel1+","+channel2+")"); System.out.println("planeZ=("+planeZ[0]+","+planeZ[1]+","+planeZ[2]+")"); System.out.println("planeX=("+planeX[0]+","+planeX[1]+","+planeX[2]+")"); System.out.println("planeXM=("+planeXM[0]+","+planeXM[1]+","+planeXM[2]+")"); } double [] v1=lensCenterVector(channel1); double [] v2=lensCenterVector(channel2); double [] interVector={0.0,0.0,0.0}; for (int i=0;i=debugThreshold) { System.out.println("interProjectionUnity=("+interProjectionUnity[0]+","+interProjectionUnity[1]+","+interProjectionUnity[2]+")"); System.out.println("horProjectionUnity=("+horProjectionUnity[0]+","+horProjectionUnity[1]+","+horProjectionUnity[2]+")"); System.out.println("vertProjectionUnity=("+vertProjectionUnity[0]+","+vertProjectionUnity[1]+","+vertProjectionUnity[2]+")"); System.out.println("cosPPRoll="+cosPPRoll+" sinPPRoll="+sinPPRoll); System.out.println("ppYaw="+ppYaw+" ppElevation="+ppElevation+" ppRoll="+ppRoll); } return result; } /** * Project lens center (entrance pupil) on the projection plane (going through the camera center) * @param channel subcamera number * @param ppYaw projection plane normal yaw in degrees, positive - CW looking from top * @param ppElevation projection plane normal elevation in degrees (up - positive) * @param ppRoll projection plane rotation around the normal, positive CW looking from the camera center * @param debugLevel debug level * @return triplet of coordinates aligned to the projection plane (z - away from the camera center) */ double [] projectPupil( int channel, double ppYaw, double ppElevation, double ppRoll){ double psi=Math.PI/180*ppRoll; double theta=Math.PI/180*ppElevation; // radians double phi=Math.PI/180*ppYaw; // radians double [] v=lensCenterVector(channel); double [][] V={{v[0]},{v[1]},{v[2]}}; /*1) rotate by -phi) around | cos(phi) 0 -sin(phi) | | 0 1 0 | | sin(phi) 0 cos(phi) | */ double [][] aR0={ {Math.cos(phi),0.0,-Math.sin(phi)}, {0.0, 1.0, 0.0}, {Math.sin(phi),0.0, Math.cos(phi)}}; Matrix R0=new Matrix(aR0); /* 2) rotate by theta around | 1 0 0 | | 0 cos(theta) -sin(theta) | | 0 sin(theta) cos(theta) | */ double [][] aR1={ {1.0, 0.0, 0.0}, {0.0, Math.cos(theta),-Math.sin(theta)}, {0.0, Math.sin(theta), Math.cos(theta)}}; Matrix R1=new Matrix(aR1); // 3) rotate by psi around projection plane z (normal) double [][] aR2={ { Math.cos(psi), -Math.sin(psi), 0.0}, { Math.sin(psi), Math.cos(psi), 0.0}, { 0.0, 0.0, 1.0}}; Matrix R2=new Matrix(aR2); double [] xyz= R2.times(R1.times(R0.times(new Matrix(V)))).getRowPackedCopy(); return xyz; } // double [] crossProduct3(double [] a,double [] b){ /** * Calculate sensor entrance pupil coordinates in camera coordinate system * @param chn sensor number * @return (x,y,z) */ public double [] lensCenterVector(int chn){ double theta=Math.PI/180*this.sensors[chn].theta; // radians double phi=Math.PI/180*this.sensors[chn].phi; // radians double azimuth=Math.PI/180*this.sensors[chn].azimuth; // radians /* 0) Translate by distance to entrance pupil (lens center) | Xc0 | | 0 | |Xc| | Yc0 | = | 0 | + |Yc| | Zc0 | | entrancePupilForward | |Zc| */ double [][] aT0={{0.0},{0.0},{this.sensors[chn].entrancePupilForward}}; Matrix T0=new Matrix(aT0); /* 2) rotate by - theta around C1X:Vc2= R2*Vc1 | Xc2 | | 1 0 0 | |Xc1| | Yc2 | = | 0 cos(theta) sin(theta) | * |Yc1| | Zc2 | | 0 -sin(theta) cos(theta) | |Zc1| */ double [][] aR2={ {1.0,0.0,0.0}, {0.0,Math.cos(theta),Math.sin(theta)}, {0.0,-Math.sin(theta),Math.cos(theta)}}; Matrix R2=new Matrix(aR2); /* 3) rotate by -(azimuth+phi) around C2Y:Vc3= R3*Vc2 | Xc3 | | cos(azimuth+phi) 0 sin(azimuth+phi) | |Xc2| | Yc3 | = | 0 1 0 | * |Yc2| | Zc3 | | -sin(azimuth+phi) 0 cos(azimuth+phi) | |Zc2| */ double [][] aR3={ {Math.cos(phi+azimuth),0.0,Math.sin(phi+azimuth)}, {0.0,1.0,0.0}, {-Math.sin(phi+azimuth),0.0,Math.cos(phi+azimuth)}}; Matrix R3=new Matrix(aR3); /* 4) Now axes are aligned, just translate to get to eyesis coordinates: Vey= T1+Vc3 | Xey | | r * sin (azimuth) | |Xc3| | Yey | = | height+centerAboveHorizontal | + |Yc3| | Zey | | r * cos (azimuth) | |Zc3| */ double [][] aT1={ {this.sensors[chn].radius*Math.sin(azimuth)}, {this.sensors[chn].height}, {this.sensors[chn].radius*Math.cos(azimuth)}}; Matrix T1=new Matrix(aT1); // MA=R3*R2; // MB=T1+R3*R2*T0; // Matrix MA=R3.times(R2); // Matrix MB=T1.plus(R3.times(R2.times(T0))); double [] aB=T1.plus(R3.times(R2.times(T0))).getRowPackedCopy(); return aB; } /** * Create a 6-slice map of the flat rectangular intersection area of two sensors * to the sensor pixel coordinates. The map is centered around the mid-point of the two * sensors on the sphere, horizontal dimension (x) from sensor1 to sensor 2. * this.panoDegreesPerPixel, this.panoLongitudeLeft, this.panoLongitudeRight, * this.panoLatitudeTop and this.panoLatitudeBottom should be already set (i.e. by * overlapPairsAreas() ). * @param channel1 first sensor channel number * @param channel2 second sensor channel number * @param angleSizeX overlap dimension in degrees (usually narrow) * @param angleSizeY overlap dimension in degrees (usually high) * @param extraPixels trim result to have only this number of non-overlapping pixels along X-axis * @param debugLevel debug level * @return image with 6 slices - {pixelX1, pixelY1, alpha1, pixelX2, pixelY2, alpha2} */ public ImagePlus interSensorOverlapMap( int channel1, int channel2, double angleSizeX, double angleSizeY, int extraPixels, String title, int debugLevel ){ // double [] interDistance={0.0}; double [][] vectors = new double [2][]; float [][][] latLong= mapInterSensorToLatLong( channel1, channel2, angleSizeX, angleSizeY, vectors, debugLevel); int height=latLong.length; int width=latLong[0].length; float [][] pixels1=mapRectangleToSensor( channel1, latLong, //(=/-90, +/- 180) debugLevel); float [][] pixels2=mapRectangleToSensor( channel2, latLong, //(=/-90, +/- 180) debugLevel); // find first and last non-empty lines // Trim result arrays int minX=width,maxX=-1,minY=height,maxY=-1; for (int iy=0;iy0.0) && (pixels2[2][index]>0.0)){ // overlap if (ix>maxX) maxX=ix; if (ixmaxY) maxY=iy; if (iymaxX){ if (debugLevel>0){ System.out.println ("interSensorOverlapMap(): No overlap width="+width+" height="+height+ " minX="+minX+" maxX="+maxX+" minY="+minY+" maxY="+maxY); } return null; } if (debugLevel>1){ System.out.println ("interSensorOverlapMap(): width="+width+" height="+height+ " minX="+minX+" maxX="+maxX+" minY="+minY+" maxY="+maxY); } minX-=extraPixels; if (minX<0) minX=0; maxX+=extraPixels; if (maxX>=width) maxX=width-1; int projectionCenterX=(width-1)/2-minX; // width is odd, symmetrical around the center int projectionCenterY=(height-1)/2-minY; // height is odd, symmetrical around the center int oWidth=maxX-minX+1; int oHeight=maxY-minY+1; float [][] pixels=new float [6][oWidth*oHeight]; for (int iy=0;iy=..>+180 for longitude * @param channel1 - number of the first camera * @param channel1 - number of the second camera * @param angleSizeX angular overlap size along the line connecting two cameras, in degrees * @param angleSizeY angular overlap size perpendicular to the line connecting two cameras, in degrees * @param vectors should be initialized as double [2][]. Returns 2 vectors: * first - unity vector, normal to the image plane, second - vector from camera1 to camera 2, mm. * Image plane axis X is a projection of this vector on the image plane * @param debugLevel debug level * @return [pixels perpendicular to line between cameras][pixels in the direction from camera 1 to camera 2]{lat, long} */ public float [][][] mapInterSensorToLatLong( int channel1, int channel2, double angleSizeX, double angleSizeY, double [][] vectors, // should be initialized as double[2][] int debugLevel ){ double lat1= this.sensors[channel1].theta; //latitude (elevation) of the first camera optical axis, degrees double long1 = this.sensors[channel1].azimuth+this.sensors[channel1].phi; //longitude (heading+azimuth) of the first camera (CW from top) adding multiple 360 is OK here double lat2 = this.sensors[channel2].theta; //latitude of the second camera double long2= this.sensors[channel2].azimuth+this.sensors[channel2].phi; // longitude of the second camera, dding multiple 360 is OK here double degreesPerPixel= this.panoDegreesPerPixel; //angular size of a pixel on equator, in degrees // camera coordinates y - up, to the target (from the camera at zero angle/zero heading), x - right (looking to the target) // converting sensor vector {0,0,1} to the camera coordinates. skipping roll double theta1= lat1* Math.PI/180; double phi1= long1*Math.PI/180; double theta2= lat2* Math.PI/180; double phi2= long2*Math.PI/180; double [][] aSensorAxis={{0.0},{0.0},{1.0}}; Matrix mSensorAxis=new Matrix(aSensorAxis); // rotate by - theta1 around sensor1 x:Vc2= R2*Vc1 double [][] aR21={ {1.0, 0.0, 0.0 }, {0.0, Math.cos(theta1), Math.sin(theta1)}, {0.0,-Math.sin(theta1), Math.cos(theta1)}}; Matrix R21=new Matrix(aR21); // rotate by -(phi1) around C2Y:Vc3= R3*Vc2 double [][] aR31={ { Math.cos(phi1), 0.0, Math.sin(phi1)}, { 0.0, 1.0, 0.0 }, {-Math.sin(phi1), 0.0, Math.cos(phi1)}}; Matrix MA1=(new Matrix(aR31)).times(R21); double [] u1=(MA1.times(mSensorAxis)).getColumnPackedCopy(); // unity vector in the direction of the first camera axis double [][] aR22={ {1.0, 0.0, 0.0 }, {0.0, Math.cos(theta2), Math.sin(theta2)}, {0.0,-Math.sin(theta2), Math.cos(theta2)}}; Matrix R22=new Matrix(aR22); // rotate by -(phi1) around C2Y:Vc3= R3*Vc2 double [][] aR32={ { Math.cos(phi2), 0.0, Math.sin(phi2)}, { 0.0, 1.0, 0.0 }, {-Math.sin(phi2), 0.0, Math.cos(phi2)}}; Matrix MA2=(new Matrix(aR32) ).times(R22); double [] u2=(MA2.times(mSensorAxis)).getColumnPackedCopy(); // unity vector in the direction of the first camera axis double [] rsltUZ={ u2[0]+u1[0], u2[1]+u1[1], u2[2]+u1[2]}; rsltUZ=unityVector(rsltUZ); // unity vector between the views of the two cameras (maybe - find center of the overlap? if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): rsltUZ={"+rsltUZ[0]+","+rsltUZ[1]+","+rsltUZ[2]+"}"); if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): radius1="+this.sensors[channel1].radius+ ", azimuth1="+this.sensors[channel1].azimuth+ ", height1="+this.sensors[channel1].height); if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): radius2="+this.sensors[channel2].radius+ ", azimuth2="+this.sensors[channel2].azimuth+ ", height2="+this.sensors[channel2].height); // lens centers locations: double [] camera1XYZ={ this.sensors[channel1].radius*Math.sin(Math.PI/180.0* this.sensors[channel1].azimuth), this.sensors[channel1].height, this.sensors[channel1].radius*Math.cos(Math.PI/180.0* this.sensors[channel1].azimuth)}; double [] camera2XYZ={ this.sensors[channel2].radius*Math.sin(Math.PI/180.0* this.sensors[channel2].azimuth), this.sensors[channel2].height, this.sensors[channel2].radius*Math.cos(Math.PI/180.0* this.sensors[channel2].azimuth)}; if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): camera1XYZ={"+camera1XYZ[0]+","+camera1XYZ[1]+","+camera1XYZ[2]+"}"); if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): camera2XYZ={"+camera2XYZ[0]+","+camera2XYZ[1]+","+camera2XYZ[2]+"}"); double [] interCamerVector={ camera2XYZ[0]-camera1XYZ[0], camera2XYZ[1]-camera1XYZ[1], camera2XYZ[2]-camera1XYZ[2]}; if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): interCamerVector={"+interCamerVector[0]+","+interCamerVector[1]+","+interCamerVector[2]+"}"); // here x,y,z - left! double [] rsltUY=unityVector(crossProduct3(rsltUZ,interCamerVector)); // double [] rsltUX= crossProduct3(interCamerVector,rsltUY); double [] rsltUX= crossProduct3(rsltUY,rsltUZ); if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): rsltUY={"+rsltUY[0]+","+rsltUY[1]+","+rsltUY[2]+"}"); if (debugLevel>1) System.out.println("mapInterSensorToLatLong(): rsltUX={"+rsltUX[0]+","+rsltUX[1]+","+rsltUX[2]+"}"); // calculate output dimensions double outPixelSize=Math.PI/180.0*degreesPerPixel; // radius =1.0 double linearXHalfRange=Math.tan(angleSizeX*Math.PI/360.0)/outPixelSize; // using half-angle double linearYHalfRange=Math.tan(angleSizeY*Math.PI/360.0)/outPixelSize; // using half-angle int xHalfSize=(int) Math.round(linearXHalfRange); int yHalfSize=(int) Math.round(linearYHalfRange); float [][][] result = new float [2*yHalfSize+1][2*xHalfSize+1][2]; for (int iy=0;iy<=2*yHalfSize;iy++) for (int ix=0;ix<=2*xHalfSize;ix++){ // double y=(iy-yHalfSize)*outPixelSize; double y=(yHalfSize-iy)*outPixelSize; // in pixel order - y-down double x=(ix-xHalfSize)*outPixelSize; double [] v={ // point on the result plane in camera coordinates x*rsltUX[0]+y*rsltUY[0]+rsltUZ[0], x*rsltUX[1]+y*rsltUY[1]+rsltUZ[1], x*rsltUX[2]+y*rsltUY[2]+rsltUZ[2], }; // convert to latitude/longitude v=unityVector(v); // re-normalize to increase precision? result[iy][ix][0]=(float) ((180.0/Math.PI)*Math.asin(v[1])); // latitude v[1] (y) - up -90..+90 result[iy][ix][1]=(float) ((180.0/Math.PI)*Math.atan2(v[0],v[2])); // longitude v[0](x) right, V[2](z) to the target +/-180 } vectors[0]=rsltUZ; vectors[1]=interCamerVector; return result; } /** * Calculate cross-product of 2 vectors in 3d * @param a 3-element vector a * @param b 3-element vector b * @return 3-element cross product of vectors a and b */ double [] crossProduct3(double [] a, double [] b){ double []cp3={ a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]}; return cp3; } /** * Calculate dot-product of 2 vectors * @param a vector a * @param b vector b * @return dot product of two vectors */ double dotProduct(double [] a, double [] b){ double dp=0; for (int i=0;i1){ System.out.println("this.sensors["+channel+"].channel].equirectangularMap.maoWOI= {x="+ woi.x+", y="+woi.y+", width="+woi.width+", height="+woi.height+"}"); System.out.println("this.panoDegreesPerPixel="+this.panoDegreesPerPixel); System.out.println("this.panoLatitudeTop="+this.panoLatitudeTop); System.out.println("this.panoLongitudeLeft="+this.panoLongitudeLeft); } double minLat=this.panoLatitudeTop-(woi.y+woi.height)*this.panoDegreesPerPixel; double maxLat=this.panoLatitudeTop-woi.y*this.panoDegreesPerPixel; double minLong=this.panoLongitudeLeft+woi.x*this.panoDegreesPerPixel; double maxLong=this.panoLongitudeLeft+(woi.x+woi.width)*this.panoDegreesPerPixel; while (minLong<-180.0) minLong+=360.0; while (minLong>=180.0) minLong-=360.0; // -180<=long<180 while (maxLong<-180.0) maxLong+=360.0; while (maxLong>=180.0) maxLong-=360.0; // -180<=long<180 boolean rollover= (maxLong<=minLong); if (debugLevel>1){ System.out.println("mapRectangleToSensor("+channel+",...): minLong="+minLong+", maxLong="+maxLong+ ", minLat="+minLat+", maxLat="+maxLat+", rollover="+rollover); } float [][] pixelCoords =new float[3][latLong.length*latLong[0].length]; for (int n=0;n1)){ System.out.println("mapRectangleToSensor("+channel+",...): lat["+iy+"]["+ix+"]="+lat+", long["+iy+"]["+ix+"]="+lng); } if ((latmaxLat)) continue; if (!rollover &&((lngmaxLong))) continue; if ( rollover &&((lngmaxLong))) continue; if (lng1)){ System.out.println("mapRectangleToSensor("+channel+",...): apy["+iy+"]["+ix+"]="+apy+", apx["+iy+"]["+ix+"]="+apx); } int iapy= (int) Math.floor(apy); int iapx= (int) Math.floor(apx); if ((iapy>= (woi.height-1)) || (iapy<0)) continue; if ((iapx>= (woi.width -1)) || (iapx<0)) continue; int index00=iapx+iapy*woi.width; if (erm.partialMap[2][index00]<=0.0) continue; // alpha is 0 - skip int indexX0=index00+1; int index0Y=index00+woi.width; int indexXY=index0Y+1; double dapy=apy-iapy; double dapx=apx-iapx; if ((iy==(latLong.length/2)) && (ix==(latLong[0].length/2)) && (debugLevel>1)){ System.out.println("mapRectangleToSensor("+channel+",...): dapy["+iy+"]["+ix+"]="+dapy+", dapx["+iy+"]["+ix+"]="+dapx); } if ((dapx>0.0) && (erm.partialMap[2][indexX0]<=0.0)) continue; if ((dapy>0.0) && (erm.partialMap[2][index0Y]<=0.0)) continue; if (((dapy>0.0) || (dapx>0.0)) && (erm.partialMap[2][indexXY]<=0.0)) continue; // bi-linear interpolate int index=iy*latLong[0].length+ix; for (int n=0;n>24) & 0xff); // alpha channel double r=K255*((iPixels[i]>>16) & 0xff); double g=K255*((iPixels[i]>> 8) & 0xff); double b=K255*( iPixels[i] & 0xff); double y=KR*r+KG*g+KB*b; aYCbCr[1][i]=y; aYCbCr[2][i]=KPB*(b-y); aYCbCr[3][i]=KPR*(r-y); } return aYCbCr; } public double [] showThresholdedDisparity( double [][][] disparityMap, int zeroShift, // 1/2 corrFFTSize double threshold ){ double [] result =new double [disparityMap.length*disparityMap[0].length]; for (int index=0;indexmax){ iMax=i; max=disparityMap[y][x][i]; } if (max>=threshold){ if ((iMax==zeroShift) || (iMax==(disparityMap[y][x].length-1))){ result[index]=iMax-zeroShift; } else { // y=ax^2+bx+c, x==0 for the middle point, c=y[0] already defined double b=0.5*(disparityMap[y][x][iMax+1]-disparityMap[y][x][iMax-1]); double a=0.5*(disparityMap[y][x][iMax+1]+disparityMap[y][x][iMax-1])-disparityMap[y][x][iMax]; // negative // double x=-0.5*b/a; result[index]=iMax-zeroShift+(-0.5*b/a); } } } } return result; } public double [] showThresholdedDisparityPixels( double [][][] disparityMap, int zeroShift, // 1/2 corrFFTSize double threshold ){ double [] map=showThresholdedDisparity( disparityMap, zeroShift, // 1/2 corrFFTSize threshold); double [] result =new double [this.mapWidth*this.mapHeight]; for (int index=0;index0.0 when calculating areas * @param debugLevel * @return For each tile (with the step corrFFTSize/overlapFraction) where defined area exceeds minimal limits return horizontal profile. For undefined tiles retuern null */ public double [][] correlateOneRow( DoubleFHT doubleFHT, boolean side, boolean autoCorrelation, int corrFFTSize, int overlapFraction, int corrYC, int maxDisparity, double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double minFracArea, boolean useBinaryAlpha, int debugLevel){ int numLayers=this.overlapImages.length/2; // int indexAlpha=0; // int indexY= 1; int indexCb=2; int indexCr=3; int numTiles=this.mapWidth*overlapFraction/corrFFTSize; if (doubleFHT==null) doubleFHT=new DoubleFHT(); double[] hamming1d=doubleFHT.getHamming1d(corrFFTSize,0.0); // pure shifted cosine, not real Hamming double [] componentWeights={ 1.0/(1.0+corrCbWeight+corrCrWeight), corrCbWeight/(1.0+corrCbWeight+corrCrWeight), corrCrWeight/(1.0+corrCbWeight+corrCrWeight)}; double [][][] selection = new double[numTiles] [][]; double [] corr=new double[corrFFTSize*corrFFTSize]; double [] frequencyFilter=null; for (int nTile=0;nTile0.0) || (correlationLowPassSigma>0.0))){ frequencyFilter=doubleFHT.createFrequencyFilter( selection[nTile][nComp+nImage*numLayers], // just for length correlationHighPassSigma, correlationLowPassSigma); } } } } } double [][] result = new double [numTiles][]; for (int nTile=0;nTile2) debugCorr=new double[span][]; for (int nTile=0;nTile2) for (int i=0;i2) for (int i=0;i=0) && (selection[numFirst][0]!=null) && (selection[numFirst][numLayers]!=null)){ for (int i=0;i0){ double rms=hFNoiseNormalize( first, // double [] data, will be modified sigma, // double filterSigma, true); // boolean centerOnly); if (debugLevel>3){ System.out.println("Correlation RMS for component "+((nComp==1)?"Y":((nComp==2)?"Cb":"Cr"))+ " was "+rms); } } for (int i=0;i=0) && (dstIndex2) { debugCorr[nDisp]=corr.clone(); System.out.println("correlateOneRow(): nTile="+nTile+" nDisp="+nDisp+" numFirst="+numFirst+" numSecond="+numSecond); } } if (debugLevel>2) { (new ShowDoubleFloatArrays()).showArrays( debugCorr, corrFFTSize, corrFFTSize, true, "corr"+nTile+"-y"+corrYC+"-MAXDSP"+maxDisparity+"-PC"+phaseCorrelationFraction); } } return result; } /** * Merge several representations of the linear features (pointed from different cells) * @param corrFFTSize size of the square tile side * @param overlapFraction Shift by this fraction of the corrFFTSize between tiles * @param features array of [row][column][parameters] describing the linear feature (see tileLineDetect() method) * @param strengthMode scale normalized linear feature to (0.0): absolute strength, 1.0 - relative strength (0.01 - several merged) * @param scaleDistances // ~1.05? compensate for decrease in measured distances to the features (partially caused by windowing) * @param debugLevel Debug level * @return array of the same format as input "featured" with some features merged and some - removed */ public double [][][] mergeLinearFeatures( int corrFFTSize, int overlapFraction, // default 8 double [][][] features, double strengthMode, // 0.0 - absolute, 1.0 - relative, 0.5 - "balanced", "-1" - use phaseStrength instead double directionTolerance, double normalDistanceTolerance, double tangentialDistanceTolerance, double hostsTolerance, double directionSigma, // make a fixed fraction of directionTolerance? double normalDistanceSigma, // make a fixed fraction of distanceTolerance? double tangentialDistanceSigma, // make a fixed fraction of distanceTolerance? int minMerged, double cellUsageShift, double scaleDistances, double swapTangentialTolerance, int swapSearchRange, int debugRow, int debugColumn, int debugLevel){ // int debugRow=144; //106; //153; //148; // int debugColumn=43; //60;// 49; //53; // int modDebugLevel=debugLevel; int height=features.length; int width=features[0].length; // now include not just same cell, but all other hosts int [][] cellUsage= getCellUsage( corrFFTSize, overlapFraction, // default 8 features, directionTolerance, normalDistanceTolerance, tangentialDistanceTolerance, hostsTolerance, scaleDistances, debugRow, debugColumn, debugLevel); // find all cells where feature is in the same cell if (debugLevel>1){ System.out.println("mergeLinearFeatures()1A:"+ " directionTolerance="+ directionTolerance+ " directionSigma="+ directionSigma+ // " kAngle="+ kAngle+ " normalDistanceTolerance="+ normalDistanceTolerance+ " tangentialDistanceTolerance="+ tangentialDistanceTolerance+ " normalDistanceSigma="+ normalDistanceSigma+ " tangentialDistanceSigma="+ tangentialDistanceSigma // +" kDist="+ kDist ); } double [][][] shares=new double [height][width][]; int [][][][] hosts= new int [height][width][][]; for (int row=0;row=0) && (swapTangentialTolerance>=0)){ for (int n=1;ndebugThreshold){ System.out.println("swapHosts()1: row="+row+" col="+col+ " row0="+row0+" col0="+col0+ " dX="+dX+" dY="+dY+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+distance); } numOutsideCell++; if (distance<0.0){ // maybe it is not needed here at all? distance=-distance; angle+=Math.PI; angle-=2*Math.PI*Math.round(angle/(2*Math.PI)); } if ((distance>maximalDistance) && // just for debug/statistics, remove border elements (row> overlapFraction/2) && (row< (height-overlapFraction/2)) && (col> overlapFraction/2) && (col< (width-overlapFraction/2))) { maximalDistance=distance; if (modDebugLevel>debugThreshold){ System.out.println("swapHosts()2: row="+row+" col="+col+ " distance="+distance+" maximalDistance"+maximalDistance); } } double sinA=Math.sin(angle); double cosA=Math.cos(angle); for (int dRow=-scanRange;dRow<=scanRange;dRow++) { int row1=row0+dRow; if ((row1>=0) && (row1=0) && (col1debugThreshold){ System.out.println("swapHosts()3: row="+row+" col="+col+ " row0="+row0+" col0="+col0+ " row1="+row1+" col1="+col1+ " cell is "+((features[row1][col1]== null)?"FREE":"USED")+ " xc="+xc+" yc="+yc+ " xc*cosA+yc*sinA="+(xc*cosA+yc*sinA)+ " row1="+row1+" col1="+col1+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+distance+ " newDist="+newDist); } } } } } } } if (bestNewDisthostStrengths[bestColRow[1]][bestColRow[0]]){ hostStrengths[bestColRow[1]][bestColRow[0]]=thisStrength; hosts[bestColRow[1]][bestColRow[0]][0]=col; hosts[bestColRow[1]][bestColRow[0]][1]=row; } if (modDebugLevel>debugThreshold){ System.out.print ("swapHosts()4: bestColRow[1]="+bestColRow[1]+" bestColRow[0]="+bestColRow[0]); System.out.print ( " hosts["+bestColRow[1]+"]["+bestColRow[0]+"][0]="+hosts[bestColRow[1]][bestColRow[0]][0]+ " hosts["+bestColRow[1]+"]["+bestColRow[0]+"][1]="+hosts[bestColRow[1]][bestColRow[0]][1]); System.out.println(" thisStrength="+thisStrength+" hostStrengths[bestColRow[1]][bestColRow[0]]="+hostStrengths[bestColRow[1]][bestColRow[0]]); } } } } } if (debugLevel>2) System.out.println(">>>> swapHosts(): numWantMove="+numWantMove+" numOutsideCell="+numOutsideCell+" maximaDistance="+maximalDistance); int [] result={numWantMove,numWantMove,numOutsideCell}; if (numWantMove==0) return result; // now move linear features to closer to them cells int numMoved=0; for (int row=0;rowdebugThreshold){ int corrYC= row*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC= col*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrYC1=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC1=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.print("swapHosts()5: "+" YC="+corrYC+" XC="+corrXC+ " row="+row+" col="+col+ " row1="+row1+" col1="+col1+" YC1="+corrYC1+" XC1="+corrXC1+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+distance); } // distance=xc*cosA+yc*sinA - distance; distance-=xc*cosA+yc*sinA; if (distance<0.0){ // maybe it is not needed here at all? distance=-distance; angle+=Math.PI; angle-=2*Math.PI*Math.round(angle/(2*Math.PI)); feature[5]=-feature[5]; } feature[3]=angle; feature[4]=distance; if (modDebugLevel>debugThreshold){ System.out.println( " new angle="+IJ.d2s(180/Math.PI*angle,2)+ " new distance="+distance); } features[row][col]=feature; cellUsage[row][col]=cellUsage[row1][col1]; cellUsage[row1][col1]=-1; numMoved++; } } result[0]=numMoved; if (debugLevel>1) System.out.println(">>>> swapHosts(): numMoved="+numMoved+" numWantMove="+numWantMove+" numOutsideCell="+numOutsideCell+" maximaDistance="+maximalDistance); return result; } private double getFeatureMatch( int row10, int col10, int row20, int col20, int gridStep, double [][][] features, double directionTolerance, double normalDistanceTolerance, double tangentialDistanceTolerance, double directionSigma, // make a fixed fraction of directionTolerance? double normalDistanceSigma, // make a fixed fraction of distanceTolerance? double tangentialDistanceSigma, // make a fixed fraction of distanceTolerance? double scaleDistances, int debugLevel){ // swap ends to make sure the result absolutely does not depend on the order in a pair int row1,row2,col1,col2; if ((row20>row10) || ((row20==row10) && (col20> col10))){ row1=row10; col1=col10; row2=row20; col2=col20; } else { row1=row20; col1=col20; row2=row10; col2=col10; } int height=features.length; int width= features[0].length; if ( (row1<0) || (col1<0) || (row1>=height) || (col1>=width) || (row2<0) || (col2<0) || (row2>=height) || (col2>=width) || (features[row1][col1]==null) || (features[row2][col2]==null)) return 0; double angle1= features[row1][col1][3]; angle1-=(2*Math.PI)*Math.round(angle1/(2*Math.PI)); double angle2= features[row2][col2][3]; angle2-=(2*Math.PI)*Math.round(angle2/(2*Math.PI)); double dAngle=angle2-angle1; dAngle-=Math.PI*Math.round(dAngle/Math.PI); if (Math.abs(dAngle)>directionTolerance) return 0; if (Math.abs(angle1-angle1)>(Math.PI/2)){ angle2+=Math.PI; angle2-=(2*Math.PI)*Math.round(angle2/(2*Math.PI)); } double angle=(angle1+angle2)/2; // here ignoring weights double sinAngle=Math.sin(angle); double cosAngle=Math.cos(angle); double distance1= scaleDistances*features[row1][col1][4]; double distance2= scaleDistances*features[row2][col2][4]; double x1=distance1*Math.cos(angle1); double y1=distance1*Math.sin(angle1); double x2=distance2*Math.cos(angle2)+gridStep*(col2-col1); double y2=distance2*Math.sin(angle2)+gridStep*(row2-row1); double dX=x2-x1; double dY=y2-y1; double dNormal=-sinAngle*dX+cosAngle*dY; if (Math.abs(dNormal)>normalDistanceTolerance) return 0; double dTangential=cosAngle*dX+sinAngle*dY; if (Math.abs(dTangential)>tangentialDistanceTolerance) return 0; if ((directionSigma<=0) || (normalDistanceSigma<=0) || (tangentialDistanceSigma<=0)) return 1.0; return Math.exp(-( dAngle*dAngle/(directionSigma*directionSigma)+ dNormal*dNormal/(normalDistanceSigma*normalDistanceSigma)+ dTangential*dTangential/(tangentialDistanceSigma*tangentialDistanceSigma) )/2.0); } private double [][][] combineFeatures( int [][][][] hosts, double [][][] shares, int [][] cellUsage, int corrFFTSize, int overlapFraction, // default 8 double [][][] features, double strengthMode, // 0.0 - absolute, 1.0 - relative, 0.5 - "balanced", "-1" - use phaseStrength instead double directionTolerance, double normalDistanceTolerance, double tangentialDistanceTolerance, double hostsTolerance, double directionSigma, // make a fixed fraction of directionTolerance? double normalDistanceSigma, // make a fixed fraction of distanceTolerance? double tangentialDistanceSigma, // make a fixed fraction of distanceTolerance? double scaleDistances, int debugRow, int debugColumn, int debugLevel){ int modDebugLevel=debugLevel; int height=features.length; int width=features[0].length; int scanAroundCells=overlapFraction/2; int gridStep=corrFFTSize/overlapFraction; double [][][] filteredFeatures = new double [height][width][]; double interCenter=corrFFTSize/overlapFraction; // find all cells where feature is in closer to the cell center, than matching feature - to the center of any other cell // int [][] theseHosts= new int [(2*scanAroundCells+1)*(2*scanAroundCells+1)][2]; // double[] theseShares=new double [(2*scanAroundCells+1)*(2*scanAroundCells+1)]; for (int row=0;row0){ // "hosts" only // modDebugLevel=debugLevel+(((row==debugRow) && (col==debugColumn))?2:0); modDebugLevel=debugLevel+(((Math.abs(row-debugRow)<=1) && (Math.abs(col-debugColumn)<=1))?2:0); double distance=scaleDistances*features[row][col][4]; double angle= features[row][col][3]; if (distance<0.0){ // maybe it is not needed here at all? distance=-distance; angle+=Math.PI; angle-=2*Math.PI*Math.round(angle/(2*Math.PI)); // phaseAtZero=-phaseAtZero; // not used here } double dX=distance*Math.cos(angle); double dY=distance*Math.sin(angle); int row0=row+ ((int) Math.round(dY/gridStep)); int col0=col+ ((int) Math.round(dX/gridStep)); if (modDebugLevel>2){ int corrYC=row*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("combineFeatures()0:, row="+row+" col="+col+" YC="+corrYC+" XC="+corrXC+ " row0="+row0+" col0="+col0+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+IJ.d2s(distance,2)+ " phase zero="+IJ.d2s(180/Math.PI*features[row][col][5],2) ); } double sumWeights=0.0, sumWDist=0.0, sumX=0.0, sumY=0.0, sumSinAngle=0.0, sumCosAngle=0.0, sumSinPhase=0.0, sumCosPhase=0.0, sumAbsStrength=0.0, sumReferenceLevel=0.0, sumPhaseStrength=0.0; for (int dRow=-scanAroundCells;dRow<=scanAroundCells;dRow++) { int row1=row0+dRow; if ((row1>=0) && (row1=0) &&(col1=0) share=shares[row1][col1][neibIndex]; // so the "energy" will be split between the hosts, preserving totals } else if ((row==row1) && (col==col1)){ share=1.0; // self } if (share>0.0){ double wDistance=getFeatureMatch( row, col, row1, col1, gridStep, features, directionTolerance, normalDistanceTolerance, tangentialDistanceTolerance, directionSigma, // make a fixed fraction of directionTolerance? normalDistanceSigma, // make a fixed fraction of distanceTolerance? tangentialDistanceSigma, // make a fixed fraction of distanceTolerance? scaleDistances, debugLevel); if (wDistance>0){ double [] feature=features[row1][col1].clone(); // compensate for negative distance if (feature[4]<0.0){ feature[4]=-feature[4]; feature[3]+=Math.PI; feature[3]-=2*Math.PI*Math.round(feature[3]/(2*Math.PI)); feature[5]=-feature[5]; // not used here } double dX1=scaleDistances*feature[4]*Math.cos(feature[3])+(interCenter* (col1-col));// dCol); double dY1=scaleDistances*feature[4]*Math.sin(feature[3])+(interCenter* (row1-row));// dRow); double dAngle=feature[3]-angle; dAngle-=2*Math.PI*Math.round(dAngle/(2*Math.PI)); if (Math.abs(dAngle)>(Math.PI/2)){ feature[3]+=Math.PI; feature[3]-=2*Math.PI*Math.round(feature[3]/(2*Math.PI)); feature[5]=-feature[5]; //phaseAtZero } double wSharedDistance=wDistance * share; double w=combinedStrength(strengthMode,features[row][col])*wSharedDistance; if (modDebugLevel>2){ int corrYC=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("combineFeatures()1:, row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC+ " strength="+features[row1][col1][0]+ " refLevel="+features[row1][col1][1]+ " angle="+IJ.d2s(180/Math.PI*features[row1][col1][3],2)+ " distance="+IJ.d2s(scaleDistances*features[row1][col1][4],2)+ " phase zero="+IJ.d2s(180/Math.PI*features[row1][col1][5],2) ); System.out.println("combineFeatures()2:, row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC+ " strength="+feature[0]+ " angle="+IJ.d2s(180/Math.PI*feature[3],2)+ " distance="+IJ.d2s(scaleDistances*feature[4],2)+ " phase zero="+IJ.d2s(180/Math.PI*feature[5],2) ); System.out.println("combineFeatures()3:, w="+w+ " wDistance="+wDistance+ " wSharedDistance="+wSharedDistance+ " dX1="+dX1+ " dY1="+dY1+ " dX="+ dX+ " dY="+ dY+ " Math.sin(feature[3])="+Math.sin(feature[3])+ " Math.cos(feature[3])="+Math.cos(feature[3])+ " Math.sin(feature[5])="+Math.sin(feature[5])+ " Math.cos(feature[5])="+Math.cos(feature[5]) ); } sumWeights+=w; sumX+=w*dX1; sumY+=w*dY1; sumSinAngle+=w*Math.sin(feature[3]); // direction sumCosAngle+=w*Math.cos(feature[3]); // direction sumSinPhase+=w*Math.sin(feature[5]); // phaseAtZero sumCosPhase+=w*Math.cos(feature[5]); // phaseAtZero sumWDist+= wSharedDistance; sumAbsStrength+= wSharedDistance*feature[0]; sumReferenceLevel+= wSharedDistance*feature[1]; sumPhaseStrength+= wSharedDistance*feature[2]; cellUsage[row][col]++; if (modDebugLevel>2){ System.out.println("combineFeatures()4:"+ " sumSinAngle="+sumSinAngle+ " sumCosAngle="+sumCosAngle+ " sumSinPhase="+sumSinPhase+ " sumCosPhase="+sumCosPhase+ " cellUsage["+row+"]["+col+"]="+cellUsage[row][col]); } } else { int corrYC=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("*** BUG: row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC); //bug } } } } } } cellUsage[row][col]--; // host was counted twice angle= Math.atan2(sumSinAngle,sumCosAngle); double phaseAtZero=Math.atan2(sumSinPhase,sumCosPhase); double x=sumX/sumWeights; double y=sumY/sumWeights; // double distance=x*Math.sin(angle)-y*Math.cos(angle); distance=x*Math.cos(angle)+y*Math.sin(angle); if (modDebugLevel>2){ System.out.println("combineFeatures()5: "+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+distance+ " phaseAtZero="+phaseAtZero+ " x="+x+ " y="+y); } if (distance<0.0){ distance=-distance; angle+=Math.PI; angle-=2*Math.PI*Math.round(angle/(2*Math.PI)); phaseAtZero=-phaseAtZero; } double [] feature={ sumAbsStrength/sumWDist, sumReferenceLevel/sumWDist, sumPhaseStrength/sumWDist, angle, distance, // here - already scaled phaseAtZero, cellUsage[row][col] }; if (modDebugLevel>2){ System.out.println("combineFeatures()6: "+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+distance+ " phaseAtZero="+phaseAtZero+ " x="+x+ " y="+y); System.out.println("combineFeatures()7:"+ " strength="+feature[0]+ " refLevel="+feature[1]+ " angle="+IJ.d2s(180/Math.PI*feature[3],2)+ " distance="+IJ.d2s(feature[4],2)+ " phase zero="+IJ.d2s(180/Math.PI*feature[5],2) ); } filteredFeatures[row][col]=feature; } } return filteredFeatures; } public double [] displayUsage( double [][][] features, int corrFFTSize, int overlapFraction, // default 8 double scale ){ double [] usage=new double[this.mapWidth*this.mapHeight]; int k=corrFFTSize/overlapFraction; for (int index=0;index6) d=features[y][x][6]; else d=1.0; } usage[index]=d*scale; } return usage; } private void getFeatureShares( int [][][][] hosts, double [][][] shares, int [][] cellUsage, int corrFFTSize, int overlapFraction, // default 8 double [][][] features, double strengthMode, // 0.0 - absolute, 1.0 - relative, 0.5 - "balanced", "-1" - use phaseStrength instead double directionTolerance, double normalDistanceTolerance, double tangentialDistanceTolerance, double hostsTolerance, double directionSigma, // make a fixed fraction of directionTolerance? double normalDistanceSigma, // make a fixed fraction of distanceTolerance? double tangentialDistanceSigma, // make a fixed fraction of distanceTolerance? double scaleDistances, int debugRow, int debugColumn, int debugLevel){ int modDebugLevel=debugLevel; int height=features.length; int width=features[0].length; int scanAroundCells=overlapFraction/2; int gridStep=corrFFTSize/overlapFraction; // find all cells where feature is in closer to the cell center, than matching feature - to the center of any other cell int [][] theseHosts= new int [(2*scanAroundCells+1)*(2*scanAroundCells+1)][2]; double[] theseShares=new double [(2*scanAroundCells+1)*(2*scanAroundCells+1)]; for (int row=0;row2){ int corrYC=row*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println(">>>>++++ getFeatureShares()1: row="+row+" col="+col+" YC="+corrYC+" XC="+corrXC+ " row0="+row0+" col0="+col0+ " distance="+distance+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " dX="+dX+ " dY="+dY); } for (int dRow=-scanAroundCells;dRow<=scanAroundCells;dRow++) { int row1=row0+dRow; if ((row1>=0) && (row1=0) && (col13){ int corrYC=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("getFeatureShares()1a: row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC+ " cellUsage[row1][col1]="+cellUsage[row1][col1]); } if (cellUsage[row1][col1]>0){ //cellUsage[row][col] - just to be sure, not needed double wDist=getFeatureMatch( row, col, row1, col1, gridStep, features, directionTolerance, normalDistanceTolerance, tangentialDistanceTolerance, directionSigma, // make a fixed fraction of directionTolerance? normalDistanceSigma, // make a fixed fraction of distanceTolerance? tangentialDistanceSigma, // make a fixed fraction of distanceTolerance? scaleDistances, debugLevel); if (wDist>0){ theseHosts[numHost][0]=col1; theseHosts[numHost][1]=row1; double w=combinedStrength(strengthMode,features[row1][col1])*wDist; theseShares[numHost]=w; sumShares+=w; numHost++; if (modDebugLevel>2){ int corrYC=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("getFeatureShares()2: row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC+ " wDist="+wDist+ " w="+w+ " sumShares="+sumShares+ " numHost="+numHost); } } else { if (modDebugLevel>2){ int corrYC=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("getFeatureShares()2: row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC+ " wDist="+wDist+ " sumShares="+sumShares+ " numHost="+numHost); } } } } } } } if (numHost>0) { hosts[row][col]=new int [numHost][2]; shares[row][col]=new double [numHost]; for (int i=0;i0 - number of cells combined, <0: 1+y*width+x of the "host" for (int row=0;row2){ int corrYC=row*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println(">>>> getCellUsage(): row="+row+" col="+col+" YC="+corrYC+" XC="+corrXC+ " row0="+row0+" col0="+col0+ " dX="+IJ.d2s(dX,3)+" dY="+IJ.d2s(dY,3)+ " range="+range+ " angle="+IJ.d2s(180/Math.PI*angle,2)+ " distance="+IJ.d2s(distance,3)+ " strength="+IJ.d2s(features[row][col][0],4)+ " refLev="+IJ.d2s(features[row][col][1],4)); } for (int dRow=-range;(dRow<=range) && best;dRow++) { int row1=row0+dRow; if ((row1>=0) && (row1=0) && (col10) &&(modDebugLevel>2)){ int corrYC=row1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; int corrXC=col1*corrFFTSize/overlapFraction+corrFFTSize/overlapFraction/2; System.out.println("getCellUsage(): row1="+row1+" col1="+col1+" YC="+corrYC+" XC="+corrXC+ " w="+w+ " best="+best+ " distance="+IJ.d2s(distance,3)+ " scaleDistances*features["+row1+"]["+col1+"]["+4+"]="+scaleDistances*features[row1][col1][4]); } if (!best) break; } } } } if (best) { cellUsage[row][col]=1; numHostCells++; } } } if (debugLevel>1){ System.out.println("mergeLinearFeatures(): Detected "+numHostCells+" host cells (common feature is closer to its center than to any other cell."); } return cellUsage; } /** * * @param corrFFTSize * @param overlapFraction * @param features * @param ignorePhase * @param strengthMode * @param phaseIntegrationWidth * @param resultHighPass * @param threadsMax * @param debugLevel * @return */ public double [] reconstructImageFeature( final int corrFFTSize, final int overlapFraction, // default 8 final double [][][] features, final boolean ignorePhase, final boolean preserveDC, final double strengthMode, // 0.0 - absolute, 1.0 - relative, 0.5 - "balanced", "-1" - use phaseStrength instead final double phaseIntegrationWidth, // use the same as during extraction? final double resultHighPass, //cutting "ribbon" near zero frequency - influences length of the detected line final int debugRow, final int debugColumn, final int threadsMax, final int debugLevel){ // final int numTileRows= this.mapHeight*overlapFraction/corrFFTSize+(((this.mapHeight*overlapFraction/corrFFTSize)*corrFFTSize/overlapFraction1)) { // System.out.println("reconstructImageFeature(): row="+row+" col="+col+" corrYC="+corrYC+" corrXC="+corrXC); // } /* if ((Math.abs(row-debugRow)<6) && (Math.abs(col-debugColumn)<6) && (feature!=null) && (debugLevel>1)){ System.out.println ("reconstructImageFeature(): tile="+tile+" ( of "+numTiles+"), row="+row+" col="+col+" corrYC="+corrYC+" corrXC="+corrXC+ " strength="+feature[0]+ " angle="+IJ.d2s(180/Math.PI*feature[3],2)+ " distance="+IJ.d2s(feature[4],2)+ " phase zero="+IJ.d2s(180/Math.PI*feature[5],2) ); } */ synchronized(this){ int indexSrc=0; for (int y=corrYC-hSize;y<(corrYC+hSize);y++){ if ((y>=0) && (y=0) && (xfeaturesPlot.length) || (indexSrc>tilePlot.length)) { // System.out.println("reconstructImageFeature(): x="+x+" y="+y+" row="+row+" col="+col+" corrYC="+corrYC+" corrXC="+corrXC+ // " indexDst="+indexDst+" featuresPlot.length="+featuresPlot.length+" indexSrc="+indexSrc+" tilePlot.length="+tilePlot.length); // } featuresPlot[indexDst]+=tilePlot[indexSrc]; } indexSrc++; indexDst++; } } else indexSrc+=corrFFTSize; } } final int numFinished=tilesFinished.getAndIncrement(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { IJ.showProgress(numFinished,numTiles); } }); } } } }; } startAndJoin(threads); IJ.showProgress(1.0); if (debugLevel>1){ System.out.println("=== reconstructImageFeature(): number of non-empty cells="+numNonEmpty.get()+ " (of "+(numTileRows*numTileColumns)+"), "+100.0*numNonEmpty.get()/((numTileRows*numTileColumns))+"%"); } return featuresPlot; } /** * Plot the extracted linear feature to a square tile * @param doubleFHT - instance of doubleFHT. if not null, will be reused and the cached data will not be re-calculated * @param corrFFTSize size of the square tile side * @param feature array of parameters describing the linear feature (see tileLineDetect() method) * @param ignorePhase always plot feature with zero phase (white on black) * @param preserveDC do not remove DC component from the plotted feature * @param strengthMode scale normalized linear feature to (0.0): absolute strength, 1.0 - relative strength (0.00.0)?( absoluteStrength/Math.pow(referenceLevel,strengthMode)):phaseStrength); if (ignorePhase) phaseAtZero=0.0; if (doubleFHT==null )doubleFHT= new DoubleFHT(); int length=corrFFTSize*corrFFTSize; int [] bandIndices= doubleFHT.getBandIndices( preserveDC, phaseIntegrationWidth, angle); double [] bandMask= doubleFHT.getRibbonMask( preserveDC, bandIndices, phaseIntegrationWidth, resultHighPass, angle); double [] phaseStepCorr=doubleFHT.compensatePhaseStep( angle, phaseAtZero, distance, 0, //phaseTolerance, // if >0 will zero amplitude if the phase is too off - - only used with non-zero modifiedAmpPhase null, // modifiedAmpPhase, // modifies phase and/or amplitude (if phaseTolerance>0), bandIndices, 0); // zeroBinHalfSize); - only used with non-zero modifiedAmpPhase double[] hamming1d=doubleFHT.getHamming1d(corrFFTSize,0.0); // complete zero out on the border double [] uniform=new double[length]; for (int iy=0;iy2){ double [] debugBandMask=new double [corrFFTSize*corrFFTSize]; for (int i=0;i2){ (new ShowDoubleFloatArrays()).showArrays( reconstructedFeature, corrFFTSize, corrFFTSize, "reconstructedFeature"); } return reconstructedFeature; } private double combinedStrength( double strengthMode, double [] feature){ return (strengthMode<-1.0)?1.0:((strengthMode>0.0)?( feature[0]/Math.pow(feature[1],strengthMode)):feature[2]); } /** * Calculate parameters of the line features in the current image * @param side 0- left, 1 - right image * @param corrFFTSize size of the square tile side * @param overlapFraction overlap tiles by this fraction of the tile size (corrFFTSize) * @param alphaThreshold Minimal required overlap of the tile with image alpha (0 - do not calculate) * @param useBinaryAlpha consider all alpha>0.0 as 1.0 * @param correlationHighPassSigma High pass filter before feature extraction (measured in frequency samples) * @param correlationLowPassSigma Low-pass filter for correlation (as fraction of the frequency range) * @param phaseIntegrationWidth - Width of the "band" when looking for the linear phase (actual will be smaller because of Hammimng window) * @param resultHighPass (used only for strength calculation) - cut frequencies near zero for the linear phase band (shortens the result line); * @param dispertionCost - >0 - use phase dispersion when calculating quality of phase match, 0 - only weights. * @param featureFilter Bitmask enabling different phase half-steps (+1 - 0, +2 - pi/2, +4 - pi, +8 - 3pi/2. Value zero allows arbitrary step * step 0 corresponds to thin white line, pi - thin black line, +pi/2 - edge black-to-white in the direction of directionAngle, * 3pi/2 - white-to-black in the direction of directionAngle * @param minRMSFrac Minimal frequency sample value relative to RMS to be considered when looking for the linear phase feature (0.0 - skip test) * @param minAbs Minimal frequency sample absolute value to be considered when looking for the linear phase feature (0.0 - skip test) * @param maxPhaseMismatch calculate phase differences for each full (above threshold) set of 4 samples (in a 2x2 square) and measure the distance * between two centers of diagonals, discard set if this distance is above threshold (0.0 - skip test) * @param calculateStrength If true, calculate feature strength by correlating the source image with the simulated linear phase band, if false (faster processing) * these two fields will be returned equal 0.0 * @param threadsMax maximal number of parallel threads to use * @param debugLevel Debug level, turning on more text/graphic output when higher * @return array [tileY][tileX] {feature parameters (or null if feature failed to be detected)}: * 0 - absolute strength of the feature (proportional to the image contrast) * 1 - Strength reference level (RMS of the frequency samples amplitudes (in the selected band in the frequency domain) * 2 - phaseStrength - composite value calculated when selecting the feature direction - this value is calculated even if calculateStrength is false * 3 - angle from the tile center to the detected feature (perpendicular to the feature direction itself), zero - left, pi/2 - down (pixel coordiantes) * 4 - distance in pixels from the tile center to the feature * 5 - phaseAtZero Detected feature type (may be constrained by featureFilter - see that parameter for meaning of the different phase values */ public double [][][] detectLinearFeatures( final boolean side, final int corrFFTSize, final int overlapFraction, // default 8 final double alphaThreshold, // minFracArea final boolean useBinaryAlpha, final double correlationHighPassSigma, final double correlationLowPassSigma, final double phaseIntegrationWidth, final double resultHighPass, //cutting "ribbon" near zero frequency - influences length of the detected line (used only for strength calculation) final double dispertionCost, final int featureFilter, final double minRMSFrac, final double minAbs, final double maxPhaseMismatch, final boolean calculateStrength, final int debugRow, final int debugColumn, final int threadsMax, final int debugLevel) { return detectLinearFeatures( side?1:0, corrFFTSize, overlapFraction, // default 8 alphaThreshold, // minFracArea useBinaryAlpha, correlationHighPassSigma, correlationLowPassSigma, phaseIntegrationWidth, resultHighPass, //cutting "ribbon" near zero frequency - influences length of the detected line (used only for strength calculation) dispertionCost, featureFilter, minRMSFrac, minAbs, maxPhaseMismatch, calculateStrength, debugRow, debugColumn, threadsMax, debugLevel); } public double [][][] detectLinearFeatures( final int imageNumber, //boolean side, final int corrFFTSize, final int overlapFraction, // default 8 final double alphaThreshold, // minFracArea final boolean useBinaryAlpha, final double correlationHighPassSigma, final double correlationLowPassSigma, final double phaseIntegrationWidth, final double resultHighPass, //cutting "ribbon" near zero frequency - influences length of the detected line (used only for strength calculation) final double dispertionCost, final int featureFilter, final double minRMSFrac, final double minAbs, final double maxPhaseMismatch, final boolean calculateStrength, final int debugRow, final int debugColumn, final int threadsMax, final int debugLevel){ if (debugLevel>1) System.out.println ("detectLinearFeatures("+imageNumber+",...)"); final int numTileRows= this.mapHeight*overlapFraction/corrFFTSize+(((this.mapHeight*overlapFraction/corrFFTSize)*corrFFTSize/overlapFraction2) System.out.println ("detectLinearFeatures(): tile="+tile+" ( of "+numTiles+"), row="+row+" col="+col+" corrYC="+corrYC+" corrXC="+corrXC); result[row][col]= tileLineDetect( doubleFHT, imageNumber, //side, alphaThreshold, useBinaryAlpha, corrFFTSize, corrXC, corrYC, correlationHighPassSigma, correlationLowPassSigma, phaseIntegrationWidth, resultHighPass, //cutting "ribbon" near zero frequency - influences length of the detected line (used only for strength calculation) dispertionCost, featureFilter, minRMSFrac, minAbs, maxPhaseMismatch, calculateStrength, debugLevel); final int numFinished=tilesFinished.getAndIncrement(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { IJ.showProgress(numFinished,numTiles); } }); // if ((result[row][col]!=null) && (debugLevel>1)) System.out.println ("detectLinearFeatures(): tile="+tile+" ( of "+numTiles+"), row="+row+" col="+col+" corrYC="+corrYC+" corrXC="+corrXC+ // " strength="+result[row][col][0]); if ((row==debugRow) && ((col==debugColumn)) && (result[row][col]!=null) && (debugLevel>1)){ System.out.println ("detectLinearFeatures(): tile="+tile+" ( of "+numTiles+"), row="+row+" col="+col+" corrYC="+corrYC+" corrXC="+corrXC+ " strength="+result[row][col][0]+ " angle="+IJ.d2s(180/Math.PI*result[row][col][3],2)+ " distance="+IJ.d2s(result[row][col][4],2)+ " phase zero="+IJ.d2s(180/Math.PI*result[row][col][5],2)+ " featureFilter="+featureFilter ); } } } }; } startAndJoin(threads); IJ.showProgress(1.0); return result; } /** * Calculate parameters of the line feature in the current image tile * @param doubleFHT - instance of doubleFHT. if not null, will be reused and the cached data will not be re-calculated * @param side 0- left, 1 - right image * @param alphaThreshold Minimal required overlap of the tile with image alpha (0 - do not calculate) * @param useBinaryAlpha consider all alpha>0.0 as 1.0 * @param corrFFTSize size of the square tile side * @param corrXC Tile center X * @param corrYC Tile center Y * @param correlationHighPassSigma High pass filter before feature extraction (measured in frequency samples) * @param correlationLowPassSigma Low-pass filter for correlation (as fraction of the frequency range) * @param phaseIntegrationWidth - Width of the "band" when looking for the linear phase (actual will be smaller because of Hammimng window) * @param resultHighPass (used only for strength calculation) - cut frequencies near zero for the linear phase band (shortens the result line); * @param dispertionCost - >0 - use phase dispersion when calculating quality of phase match, 0 - only weights. * @param featureFilter Bitmask enabling different phase half-steps (+1 - 0, +2 - pi/2, +4 - pi, +8 - 3pi/2. Value zero allows arbitrary step * step 0 corresponds to thin white line, pi - thin black line, +pi/2 - edge black-to-white in the direction of directionAngle, * 3pi/2 - white-to-black in the direction of directionAngle * @param minRMSFrac Minimal frequency sample value relative to RMS to be considered when looking for the linear phase feature (0.0 - skip test) * @param minAbs Minimal frequency sample absolute value to be considered when looking for the linear phase feature (0.0 - skip test) * @param maxPhaseMismatch calculate phase differences for each full (above threshold) set of 4 samples (in a 2x2 square) and measure the distance * between two centers of diagonals, discard set if this distance is above threshold (0.0 - skip test) * @param calculateStrength If true, calculate feature strength by correlating the source image with the simulated linear phase band, if false (faster processing) * these two fields will be returned equal 0.0 * @param debugLevel Debug level, turning on more text/graphic output when higher * @return array with the following result fields (or null if feature failed to be detected): * 0 - absolute strength of the feature (proportional to the image contrast) * 1 - Strength reference level (RMS of the frequency samples amplitudes (in the selected band in the frequency domain) * 2 - phaseStrength - composite value calculated when selecting the feature direction - this value is calculated even if calculateStrength is false * 3 - angle from the tile center to the detected feature (perpendicular to the feature direction itself), zero - left, pi/2 - down (pixel coordiantes) * 4 - distance in pixels from the tile center to the feature * 5 - phaseAtZero Detected feature type (may be constrained by featureFilter - see that parameter for meaning of the different phase values */ public double [] tileLineDetect( DoubleFHT doubleFHT, int imageNumber, //boolean side, double alphaThreshold, final boolean useBinaryAlpha, int corrFFTSize, int corrXC, int corrYC, double correlationHighPassSigma, double correlationLowPassSigma, double phaseIntegrationWidth, double resultHighPass, //cutting "ribbon" near zero frequency - influences length of the detected line (used only for strength calculation) double dispertionCost, int featureFilter, double minRMSFrac, double minAbs, double maxPhaseMismatch, boolean calculateStrength, int debugLevel){ if (doubleFHT==null )doubleFHT= new DoubleFHT(); int length=corrFFTSize*corrFFTSize; int numSensors=this.channel.length; int numLayers=this.overlapImages.length/numSensors; double [][] selection = getSelection( 0, // cross-correlation always corrFFTSize, corrXC, corrYC, 0); // positive - shift second image, negative - shift first image double[] hamming1d=doubleFHT.getHamming1d(corrFFTSize,0.0); // complete zero out on the border double [] window=new double[length]; // TODO:cache window in doubleFHT instance? for (int iy=0;iy0.0){ double sumAlpha=0.0; double sumWindow=0.0; for (int index=0;index0.0) || (correlationLowPassSigma>0.0)){ frequencyFilter=doubleFHT.createFrequencyFilter( tileData, // just for length correlationHighPassSigma, correlationLowPassSigma); } //calculateAmplitude doubleFHT.swapQuadrants(tileData); doubleFHT.transform(tileData,false); if (frequencyFilter!=null){ doubleFHT.multiplyByReal(tileData, frequencyFilter); } doubleFHT.debug=(debugLevel>3); double [] dirDistStrength= doubleFHT.calcPhaseApproximation( phaseIntegrationWidth, tileData, minRMSFrac, minAbs, maxPhaseMismatch, dispertionCost, featureFilter); if (dirDistStrength==null) return null; double angle= dirDistStrength[0]; double distance= dirDistStrength[1]; double phaseAtZero= dirDistStrength[2]; double phaseStrength=dirDistStrength[3]; if (distance<0){ angle+=Math.PI; angle+=(2*Math.PI)*Math.round(angle/(2*Math.PI)); phaseAtZero=-phaseAtZero; distance=-distance; } double [] result= { Double.NaN, //strengths[0], // absolute strength Double.NaN, //strengths[1], // reference level phaseStrength, // phase strength (less computations than the first two, known anyway) angle, distance, phaseAtZero }; if (!calculateStrength) return result; // needed data is already received, next just for debugging. Not only - also to measure the strength int [] bandIndices= doubleFHT.getBandIndices( false, phaseIntegrationWidth, angle); double [] bandMask= doubleFHT.getRibbonMask( false, bandIndices, phaseIntegrationWidth, resultHighPass, angle); double [] phaseStepCorr=doubleFHT.compensatePhaseStep( angle, phaseAtZero, distance, 0, //phaseTolerance, // if >0 will zero amplitude if the phase is too off - - only used with non-zero modifiedAmpPhase null, // modifiedAmpPhase, // modifies phase and/or amplitude (if phaseTolerance>0), bandIndices, 0); // zeroBinHalfSize); - only used with non-zero modifiedAmpPhase double [] strengths=doubleFHT.linearFeatureStrength( tileData, angle, distance, phaseIntegrationWidth, bandIndices, null, // modifiedAmpPhase, phaseStepCorr, bandMask); result[0]=strengths[0]; // absolute strength result[1]=strengths[1]; // reference level if (debugLevel>2){ System.out.println("balanced strength="+IJ.d2s(Math.sqrt(strengths[0]*strengths[0]/strengths[1]),3)); System.out.println("absolute strength="+IJ.d2s(strengths[0],3)); System.out.println("relative strength="+IJ.d2s((strengths[0]/strengths[1]),3)); System.out.println(" phase strength="+IJ.d2s(phaseStrength,3)); System.out.println(" Angle="+IJ.d2s(180*angle/Math.PI,2)+" degrees"); System.out.println(" distance="+IJ.d2s(distance,2)+" pixels"); System.out.println(" zero phase="+IJ.d2s(180*phaseAtZero/Math.PI,2)+" degrees"); } return result; } public void testLineDetect( int corrFFTSize, int corrXC, int corrYC, double phaseCorrelationFraction, double correlationHighPassSigma, double correlationLowPassSigma, boolean removeIslands, double threshold, // relative to RMS value boolean adjustDirection, double phaseIntegrationWidth, double resultHighPass, double dispertionCost, int featureFilter, double zeroBinHalfSize, double minRMSFrac, double minAbs, double maxPhaseMismatch, double phaseTolerance, //simulation phase tolerance int debugLevel){ int length=corrFFTSize*corrFFTSize; DoubleFHT doubleFHT= new DoubleFHT(); int numLayers=this.overlapImages.length/2; double [][] selection = getSelection( 1, // autocorrelation always corrFFTSize, corrXC, corrYC, 0); // positive - shift second image, negative - shift first image double[] hamming1d=(new DoubleFHT()).getHamming1d(corrFFTSize,0.0); // complete zero out on the border double [] window=new double[length]; for (int iy=0;iy2){ (new ShowDoubleFloatArrays()).showArrays( first, corrFFTSize, corrFFTSize, "windowed-Y"+corrXC+"-y"+corrYC+"-PC"+phaseCorrelationFraction); } double [] frequencyFilter=null; if ((correlationHighPassSigma>0.0) || (correlationLowPassSigma>0.0)){ frequencyFilter=doubleFHT.createFrequencyFilter( first, // just for length correlationHighPassSigma, correlationLowPassSigma); } //calculateAmplitude doubleFHT.swapQuadrants(first); doubleFHT.transform(first,false); if (frequencyFilter!=null){ doubleFHT.multiplyByReal(first, frequencyFilter); if (debugLevel>2){ double [] filteredAmplitude=doubleFHT.calculateAmplitude(first); (new ShowDoubleFloatArrays()).showArrays( filteredAmplitude, corrFFTSize, corrFFTSize, "filteredAmp-Y"+corrXC+"-y"+corrYC+"-PC"+phaseCorrelationFraction); for (int i=0;i1); double [] dirDistStrength= doubleFHT.calcPhaseApproximation( phaseIntegrationWidth, first, minRMSFrac, minAbs, maxPhaseMismatch, dispertionCost, featureFilter); if (dirDistStrength==null) return; double angle= dirDistStrength[0]; double distance= dirDistStrength[1]; double phaseAtZero= dirDistStrength[2]; double phaseStrength=dirDistStrength[3]; // needed data is already received, next just for debugging int [] bandIndices= doubleFHT.getBandIndices( false, phaseIntegrationWidth, angle); double [] bandMask= doubleFHT.getRibbonMask( false, bandIndices, phaseIntegrationWidth, resultHighPass, angle); // double [][] modifiedAmpPhase=doubleFHT.calcIndAmHPhase (first, bandIndices); double [] phaseStepCorr=doubleFHT.compensatePhaseStep( angle, phaseAtZero, distance, phaseTolerance, // if >0 will zero amplitude if the phase is too off null, // modifiedAmpPhase, // modifies phase and/or amplitude (if phaseTolerance>0), bandIndices, zeroBinHalfSize); double [] strengths=doubleFHT.linearFeatureStrength( first, angle, distance, phaseIntegrationWidth, bandIndices, null, // modifiedAmpPhase, phaseStepCorr, bandMask); if (debugLevel>0){ System.out.println("balanced strength="+IJ.d2s(Math.sqrt(strengths[0]*strengths[0]/strengths[1]),3)); System.out.println("absolute strength="+IJ.d2s(strengths[0],3)); System.out.println("relative strength="+IJ.d2s((strengths[0]/strengths[1]),3)); System.out.println(" phase strength="+IJ.d2s(phaseStrength,3)); System.out.println(" Angle="+IJ.d2s(180*angle/Math.PI,2)+" degrees"); System.out.println(" distance="+IJ.d2s(distance,2)+" pixels"); System.out.println(" zero phase="+IJ.d2s(180*phaseAtZero/Math.PI,2)+" degrees"); } if (debugLevel>0){ double [] reconstructed=doubleFHT.reconstructLinearFeature( ones, angle, distance, phaseIntegrationWidth, bandIndices, null, //modPhase, phaseStepCorr, bandMask); //debugWindowed double [][] reconstructionComparison={reconstructed, debugWindowed}; String [] titles={"reconstructed","windowed original"}; (new ShowDoubleFloatArrays()).showArrays( reconstructionComparison, corrFFTSize, corrFFTSize, true, "reconstructed-x"+corrXC+"-y"+corrYC, titles); } } public void testLineDetectDebug( int corrFFTSize, int corrXC, int corrYC, double phaseCorrelationFraction, double correlationHighPassSigma, double correlationLowPassSigma, boolean removeIslands, double threshold, // relative to RMS value boolean adjustDirection, double phaseIntegrationWidth, double resultHighPass, double dispertionCost, int featureFilter, double zeroBinHalfSize, double minRMSFrac, double minAbs, double maxPhaseMismatch, double phaseTolerance, //simulation phase tolerance int debugLevel){ int length=corrFFTSize*corrFFTSize; DoubleFHT doubleFHT= new DoubleFHT(); // double [][] selection = new double[this.overlapImages.length][length]; int numLayers=this.overlapImages.length/2; double [][] selection = getSelection( 1, // autocorrelation always corrFFTSize, corrXC, corrYC, 0); // positive - shift second image, negative - shift first image // correlation stuff // double[] hamming1d=(new DoubleFHT()).getHamming1d(corrFFTSize); double[] hamming1d=(new DoubleFHT()).getHamming1d(corrFFTSize,0.0); // complete zero out on the border double [] window=new double[length]; for (int iy=0;iy2){ (new ShowDoubleFloatArrays()).showArrays( first, corrFFTSize, corrFFTSize, "windowed-Y"+corrXC+"-y"+corrYC+"-PC"+phaseCorrelationFraction); } double [] frequencyFilter=null; if ((correlationHighPassSigma>0.0) || (correlationLowPassSigma>0.0)){ frequencyFilter=doubleFHT.createFrequencyFilter( first, // just for length correlationHighPassSigma, correlationLowPassSigma); } //calculateAmplitude doubleFHT.swapQuadrants(first); doubleFHT.transform(first,false); if (debugLevel>2){ double [] unfilteredAmplitude=doubleFHT.calculateAmplitude(first); for (int i=0;i2){ double [] filteredAmplitude=doubleFHT.calculateAmplitude(first); (new ShowDoubleFloatArrays()).showArrays( filteredAmplitude, corrFFTSize, corrFFTSize, "filteredAmp-Y"+corrXC+"-y"+corrYC+"-PC"+phaseCorrelationFraction); for (int i=0;i1); double [] dirDistStrength= doubleFHT.calcPhaseApproximation( phaseIntegrationWidth, first, minRMSFrac, minAbs, maxPhaseMismatch, dispertionCost, featureFilter); if (dirDistStrength==null) return; double angle= dirDistStrength[0]; double distance= dirDistStrength[1]; double phaseAtZero= dirDistStrength[2]; // double phaseStrength=dirDistStrength[3]; // needed data is already received, next just for debugging int [] bandIndices= doubleFHT.getBandIndices( false, phaseIntegrationWidth, angle); double [] bandMask= doubleFHT.getRibbonMask( false, bandIndices, phaseIntegrationWidth, resultHighPass, angle); double [][] modifiedAmpPhase=doubleFHT.calcIndAmHPhase (first, bandIndices); double [] phaseStepCorr=doubleFHT.compensatePhaseStep( angle, phaseAtZero, distance, phaseTolerance, // if >0 will zero amplitude if the phase is too off modifiedAmpPhase, // modifies phase and/or amplitude (if phaseTolerance>0), bandIndices, zeroBinHalfSize); if (debugLevel>0){ double [][] dbgMask=new double [2][corrFFTSize*corrFFTSize/2]; for (int i=0;i0){ System.out.println(" ====== Linear feature strength: absolute="+strengths[0]+" relative="+(strengths[0]/strengths[1])); } } public double [] correlateRectangular( boolean autoCorrelation, int corrFFTSize, int overlapFraction, int corrXC, int corrYC, int maxDisparity, double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double contrastThreshold, boolean useBinaryAlpha, boolean enableNegativeDisparity, int threadsMax, int debugLevel){ int numTiles=1+(maxDisparity/(corrFFTSize/overlapFraction)); int length=corrFFTSize*corrFFTSize; int corrWidth=corrFFTSize + corrFFTSize*(numTiles-1)/overlapFraction; int outLength=corrFFTSize*corrWidth; double [] fullCorr=new double [outLength]; for (int i=0;i0){ System.out.println("Selection ("+(useBinaryAlpha?"binary":"analog")+" alpha ) xc="+corrXC+" yc="+corrYC+" size="+corrFFTSize+" maxDisparity="+maxDisparity+ " : first "+IJ.d2s(100*stats[0],3)+"%, second "+IJ.d2s(100*stats[1],3)+"%, overlap "+IJ.d2s(100*stats[2],3)+"%"); } double [][] window={selection[0].clone(),selection[numLayers].clone()}; for (int iy=0;iy1)){ debugCorr[1]=first.clone(); debugCorr[2]=second.clone(); } // TODO: use common (per-thread) DoubleFHT instance to use caching of sin,cos, filter tables corr[i+1]=(new DoubleFHT()).correlate ( first, second, correlationHighPassSigma, correlationLowPassSigma, phaseCorrelationFraction); double sigma=(i==0)?noiseNormalizationSignaY:noiseNormalizationSignaCbCr; if (sigma>0){ double rms=hFNoiseNormalize( corr[i+1], // double [] data, sigma, // double filterSigma, true); // boolean centerOnly); if (this.debugLevel>1){ System.out.println("Correlation RMS for component "+((i==0)?"Y":((i==1)?"Cb":"Cr"))+ " was "+rms); } } // for (int j=0;j2){ String [] titles={"corr","first","second"}; debugCorr[0]=corr[0]; (new ShowDoubleFloatArrays()).showArrays( debugCorr, corrFFTSize, corrFFTSize, true, "corr"+nTile+"_selection-x"+corrXC+"-y"+corrYC+"-MAXDSP"+maxDisparity+"-PC"+phaseCorrelationFraction, titles); } int srcIndex=0; for (int y=0;y>=1) if ((chm & 1)!=0) numLayers++; if (externalData!=null) numLayers++; int [] img= {first,second}; int [] channels=new int [numLayers]; int c=0; int l=0; for (int chm=channelMask; chm!=0; chm>>=1) { if ((chm & 1)!=0) channels[l++]=c; c++; } if (externalData!=null) channels[l]=3; if (debugLevel>1){ double d=Math.sqrt(dxA*dxA+dyA*dyA); System.out.println(" getShiftedSlices(): xc1="+xc1+" yc1="+yc1+" size="+size+" first="+first+" second="+second); System.out.println(" getShiftedSlices(): d="+IJ.d2s(d,2)+" dX="+IJ.d2s(dxA,2)+" dY="+IJ.d2s(dyA,2)); System.out.println(" getShiftedSlices(): shiftImage[0]="+shiftImage[0]+" shiftImage[1]="+shiftImage[1]); System.out.println(" getShiftedSlices() first image: dX="+IJ.d2s(dx[0],2)+" dY="+IJ.d2s(dy[0],2)); System.out.println(" getShiftedSlices() second image: dX="+IJ.d2s(dx[1],2)+" dY="+IJ.d2s(dy[1],2)); System.out.println(" getShiftedSlices() second image: iDx="+iDx+" iDyY="+iDy); System.out.println(" getShiftedSlices() second image: xc="+(xc1+iDx)+" yc="+(yc1+iDy)); } int doubleSize=2*size; int doubleLength=doubleSize*doubleSize; double [] slice= new double[doubleLength]; double [][] result=new double [2*numLayers][]; double [] imgSlice; if (window==null) { window = new double [doubleLength]; double[] window1d=doubleFHT.getHamming1d(size); // Hamming int index=0; int halfSize=size/2; int size32=3*size/2; for (int iy=0;iysize32)?window1d[iy-size]:1.0); for (int ix=0;ixsize32)?window1d[ix-size]:1.0); window[index++]=wx*wy; } } } if (debugLevel>1){ // double d=Math.sqrt(dxA*dxA+dyA*dyA); System.out.print("\ngetShiftedSlices(): channelMask="+channelMask+" channels="); for (int i=0;i2){ (new ShowDoubleFloatArrays()).showArrays( window, doubleSize, doubleSize, "window"); } } for (int iImg=0;iImg=0) && (chn<3)){ // starts from 0 (Y) imgSlice=this.overlapImages[numImgChannels*nImg+chn+1]; // skip alpha if (debugLevel>2) System.out.println("Using slice "+(numImgChannels*nImg+chn+1)); } else { imgSlice=externalData[nImg]; if (debugLevel>2) System.out.println("Using externalData["+nImg+"]"); } if (debugLevel>2) System.out.println("xc["+iImg+"]="+xc[iImg]+" yc["+iImg+"]="+yc[iImg]+" shiftImage["+iImg+"]="+shiftImage[iImg]); slice= getSelection( imgSlice, // source image/channel slice slice, doubleSize, //int width, doubleSize, //int height, xc[iImg] , //xc, yc[iImg]); //yc); if (debugLevel>2) { (new ShowDoubleFloatArrays()).showArrays( slice, doubleSize, doubleSize, "slice"); } // normalize and save DC double dc=0.0; if (shiftImage[iImg]){ dc=normalizeAndWindowGetDC (slice, window); //windowInterpolation // doubleFHT.shift(slice, dx[iImg], dy[iImg]); doubleFHT.shift(slice, -dx[iImg], -dy[iImg]); } int oSliceNumber=img.length*cN+iImg; if (doubleSizeOutput) { if (debugLevel>2) System.out.println("doubleLength="+doubleLength+"doubleSize="+doubleSize); result[oSliceNumber]=new double [doubleLength]; int oIndex=0; for (int iY=0;iY=result[oSliceNumber].length) || (oIndex>=slice.length) || (oIndex>=window.length)){ System.out.println("iX="+iX+" iY="+iY+" oIndex="+oIndex); } // result[oSliceNumber][oIndex++]=imgSlice[oIndex]/window[oIndex]+dc;// no NaN with Hamming result[oSliceNumber][oIndex]=slice[oIndex]/window[oIndex]+dc;// no NaN with Hamming oIndex++; } } }else{ result[oSliceNumber]=new double [size*size]; int iIndex=size*size+size/2; // top left index of the inner square size*size int oIndex=0; for (int iY=0;iY2){ String [] channelNames={"Y","Cb","Cr","Lin"}; String [] titles=new String[2*numLayers]; String title="SH"; for (int i=0;i=nImg) nSImg++; // here - second image number, not index! imp_stack.setProperty("firstImage_"+nPair, ""+nImg); imp_stack.setProperty("secondImage_"+nPair, ""+nSImg); } if (this.disparityScales!=null) { imp_stack.setProperty("numberOfImages", this.disparityScales.length+""); for (int i=0;ipairs[nPair][0])?(pairs[nPair][1]-1):pairs[nPair][1]; } this.corrFFTSize= Integer.parseInt ((String) imp.getProperty("corrFFTSize")); this.overlapStep= Integer.parseInt ((String) imp.getProperty("overlapStep")); this.disparityPerEntry= Double.parseDouble ((String) imp.getProperty("disparityPerEntry")); this.tilesY= Integer.parseInt ((String) imp.getProperty("tilesY")); this.tilesX= Integer.parseInt ((String) imp.getProperty("tilesX")); if (imp.getProperty("numberOfImages")==null){ // later - add to required properties, now just to use older files int numImages=-1; for (int i=0;inumImages) numImages=pairs[i][0]; if (pairs[i][1]>numImages) numImages=pairs[i][1]; } numImages++; double [][] disparityPair={{-0.5,0.0},{0.5,0.0}}; double [][] disparityTriplet={{0.0,Math.sqrt(3)/3}, {-0.5,-Math.sqrt(3)/6},{0.5,-Math.sqrt(3)/6}}; if ((numImages==2)|| (numImages==3)){ this.disparityScales=new double [numImages][2]; for (int i=0;i2 ) && (tile== ( (tiles/tilesX/2)*tilesX +(tilesX/2))); if (debugThis){ System.out.println("tile="+tile+" tiles="+tiles+" tilesX="+tilesX); } for (int nPair=0;nPair2){ (new ShowDoubleFloatArrays()).showArrays( this.centerPixels, this.impDisparity.getWidth(), this.centerPixels.length/this.impDisparity.getWidth(), "center-corr"); } } /** * Calculating combined correlation arrays for the actual cameras from the center camera * Will do nothing if the data is already calcualted * @param threadsMax maximal number of therads to use * @param showProgress show ImageJ progress/status * @param debugLevel debug level */ public void moveDisparityFromCenter( final int threadsMax, final boolean showProgress, final int debugLevel){ if (this.syntheticPixels!=null) return; // already done if (this.centerPixels==null) { String msg="Centered disparity data is not defined"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } final int numImages= this.disparityScales.length; this.syntheticPixels=new float [numImages] [this.centerPixels.length]; // all should be the same // TODO - convert to multithread? final int tilesPerImage=this.tilesY*this.tilesX; final int tiles=tilesPerImage*numImages; final int tilesX=this.tilesX; final double [][] disparityScales=this.disparityScales; final Thread[] threads = newThreadArray(threadsMax); final AtomicInteger tileIndexAtomic = new AtomicInteger(0); final float [] centerPixels=this.centerPixels; final float [][] syntheticPixels=this.syntheticPixels; if (showProgress) IJ.showProgress(0.0); if (showProgress) IJ.showStatus("Re-centering correlation data..."); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { for (int tile=tileIndexAtomic.getAndIncrement(); tile2 ) && (nImage==0)&& (tile== ( (tiles/tilesX/2)*tilesX +(tilesX/2))); if (debugThis){ System.out.println("nImage="+nImage+"tile="+tile+" tiles="+tiles+" tilesX="+tilesX); } double [] dXY={-disparityScales[nImage][0],-disparityScales[nImage][1]}; if (debugThis){ System.out.println("\n\nnImg="+nImage+" dXY[0]="+dXY[0]+" dXY[1]="+dXY[1]); } double [] tileData=getRecenteredDisparity( tileX, //int tileX, tileY, //int tileY, centerPixels, dXY, debugThis?(debugLevel+2):debugLevel); if (tileData==null) System.out.println("combineDisparityToCenter(): tileData==null, tile="+tile); setTileDisparity( tileData, tileX, tileY, syntheticPixels[nImage]); if (showProgress){ final int fTile=tile; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // Here, we can safely update the GUI // because we'll be called from the // event dispatch thread IJ.showProgress(fTile,tiles); } }); } } } }; } startAndJoin(threads); if (showProgress) IJ.showStatus(""); if (showProgress) IJ.showProgress(1.0); } private void initDoubleTileWindow(){ int size=this.overlapStep*2; this.doubleTileWindow = new double [size*size]; double [] window1d=(new DoubleFHT()).getHamming1d(size,0); // pure cosine for (int i=0;i3) System.out.println("getRecenteredDisparity("+tileX+","+tileY+",pixels,...)"); int disparityPoints=getDisparityPoints(); double disparityPerEntry=getDisparityPerEntry(); double lDisp=Math.sqrt(relDxy[0]*relDxy[0]+relDxy[1]*relDxy[1])*(disparityPoints-1)*disparityPerEntry; // in Pixels int maxTileDis=(int) Math.floor(lDisp/this.overlapStep)+2; // 1 is enough, maybe some rounding? double [][][] tileCache=new double [2*maxTileDis+1][2*maxTileDis+1][]; for (int i=0;i3) System.out.print(" maxTileDis="+maxTileDis); for (int i=0;i3) System.out.print("getRecenteredDisparity() i="+i); double dTX=kTXY[0]*i; // tile period is 0.5 in this units, all tiles within -1.0...+1.0 may contribute - normally 4 double dTY=kTXY[1]*i; double d=0.0; double weight=0.0; int idTX0=(int) Math.floor(dTX); int idTY0=(int) Math.floor(dTY); for (int iTY=0;iTY<2;iTY++) { int iTYCache=idTY0+iTY+maxTileDis; int pY= (int) Math.round ((dTY-(idTY0+iTY)+1.0)*this.overlapStep); // 0... 2*this.overlapStep int srcTY=tileY+idTY0+iTY; if ((srcTY>=0) && (srcTY=0) && (pY=0) && (srcTX=0) && (pX3) || ((tileX+idTX0+iTX)<0) || ((tileY+idTY0+iTY)<0)){ System.out.println ("\n>>>getting tile to cache: iTXCache="+iTXCache+" iTYCache="+iTYCache+" tileX="+tileX+" tileY="+tileY+" idTX0="+idTX0+" idTY0="+idTY0+ " iTX="+iTX+" iTY="+iTY); } tileCache[iTYCache][iTXCache]=getTileDisparity( // negative? srcTX, srcTY, tiledCorrelation); } double w=this.doubleTileWindow[pY*windowSize+pX]; weight+=w; d+=w*tileCache[iTYCache][iTXCache][i]; if (debugLevel>3) { System.out.print(" pX="+pX+" pY="+pY+" srcTX="+srcTX+" srcTY="+srcTY+" d="+tileCache[iTYCache][iTXCache][i]+" w="+w); } } } } result[i]=(weight>0)?(d/weight):0.0; if (debugLevel>3) System.out.println(" weight="+weight+" result["+i+"]="+result[i]); } if (debugLevel>3) { for (int i=0;i=data.length) || (i>=disparityArray.length)){ System.out.println("setTileDisparity(disparityArray,"+itx+","+ity+", data): index="+index+ " data.length="+data.length+" i="+i+" disparityArray.length="+disparityArray.length+" disparityPoints="+disparityPoints+" width="+width); } data[index++]=(float) disparityArray[i]; //oob 20375037 } } public double [] getCorrelationArray( double x, double y, double fatZero, int threadsMax, boolean showProgress, int debugLevel ){ combineDisparityToCenter( fatZero, threadsMax, showProgress, debugLevel); return getCorrelationArray( this.centerPixels, x, y, threadsMax, showProgress, debugLevel ); } public double [] getCorrelationArray( int imageNumber, double x, double y, int threadsMax, boolean showProgress, int debugLevel ){ moveDisparityFromCenter( threadsMax, showProgress, debugLevel); float [] data=this.syntheticPixels[imageNumber]; return getCorrelationArray( data, x, y, threadsMax, showProgress, debugLevel ); } public double [] getCorrelationArray( int firstImage, int secondImageIndex, // double fatZero, double x, double y, int threadsMax, boolean showProgress, int debugLevel ){ int nPair; nPair= imagesToNPair(firstImage, secondImageIndex); if (nPair<0) { System.out.println("getCorrelationArray(): No data for pair of images "+firstImage+" and second image index "+secondImageIndex+ " (second image = "+((secondImageIndex>=firstImage)?(secondImageIndex+1):(secondImageIndex))+")"); return null; } float [] data=(nPair>=0)?this.pixels[nPair]:this.centerPixels; return getCorrelationArray( data, x, y, threadsMax, showProgress, debugLevel ); } private double [] getCorrelationArray( float [] data, double x, double y, int threadsMax, boolean showProgress, int debugLevel ){ double tx=x/this.overlapStep; double ty=y/this.overlapStep; int itx0=(int) Math.floor(tx); int ity0=(int) Math.floor(ty); tx-=itx0; ty-=ity0; int itx1=itx0+1; int ity1=ity0+1; if ((itx1<0) || (itx0>=this.tilesX) || (ity1<0) || (ity0>=this.tilesY)) { System.out.println("getCorrelationArray(): this.overlapStep="+this.overlapStep+" this.tilesX="+this.tilesX+"this.tilesY="+this.tilesY); System.out.println("getCorrelationArray(): itx0="+itx0+" ity0="+ity0+" out of bounds. x="+x+" y="+y); return null; } if (itx0<0){itx0=itx1;tx=0; } if (itx1>=this.tilesX){itx1=itx0;tx=0;} if (ity0<0){ity0=ity1;ty=0; } if (ity1>=this.tilesY){ity1=ity0;ty=0;} int width=this.impDisparity.getWidth(); int disparityPoints=width/this.tilesX; int index00=ity0*width+itx0*disparityPoints; int index10=ity0*width+itx1*disparityPoints; int index01=ity1*width+itx0*disparityPoints; int index11=ity1*width+itx1*disparityPoints; double [] result=new double [disparityPoints]; for (int i=0;iresult.length) || ((index00+i)>=data.length) || ((index10+i)>=data.length) || ((index01+i)>=data.length) || ((index11+i)>=data.length)){ System.out.println("width="+width+" index00="+index00+" index10="+index10+" index01="+index01+" index11="+index11); System.out.println("itx0="+itx0+" itx1="+itx1+" ity0="+ity0+" ity1="+ity1+" this.tilesX="+this.tilesX+" this.tilesY="+this.tilesY); } result[i]= // bi-linear interpolation //TODO:java.lang.ArrayIndexOutOfBoundsException: 24760512at PixelMapping$InterSensor$DisparityTiles.getCorrelationArray(PixelMapping.java:4619) (data[index00+i]*(1.0-tx)+data[index10+i]*tx)*(1.0-ty)+ (data[index01+i]*(1.0-tx)+data[index11+i]*tx)*ty; } return result; } public double [] getCorrelationArray( int imageNumber, double fatZero, double x, double y, int threadsMax, boolean showProgress, int debugLevel ){ int numSecondImages=0; for (int i=0;i0) d*=(partial[nI][nP]+fatZero); else d=0; } result[nP]=Math.pow(d, pwr)-fatZero; } return result; } private int imagesToNPair(int firstImage, int secondImageIndex){ for (int i=0;i=0)?woi.x:0; this.zMapWOI.y=(woi.y>=0)?woi.y:0; this.zMapWOI.width= (((woi.x+woi.width) <=this.tilesX)?(woi.x+woi.width): this.tilesX)-this.zMapWOI.x; this.zMapWOI.height=(((woi.y+woi.height)<=this.tilesY)?(woi.y+woi.height):this.tilesY)-this.zMapWOI.y; final Rectangle zMapWOI=this.zMapWOI; if ((this.zMap==null) || (this.zMap[nImg]==null)) initZMap(nImg); final ZTile [][] thisZMap=this.zMap[nImg]; final int tilesX=this.zMapWOI.width; final int tiles=this.zMapWOI.width*this.zMapWOI.height; if (debugLevel>2) System.out.println("fillForegroundGaps() woi.x="+woi.x+" woi.y="+woi.y+ " woi.width="+woi.width+" woi.height="+woi.height); if (debugLevel>2) System.out.println("fillForegroundGaps() zMapWOI.x="+zMapWOI.x+" zMapWOI.y="+zMapWOI.y+ " zMapWOI.width="+zMapWOI.width+" zMapWOI.height="+zMapWOI.height); if (debugLevel>1) System.out.println("fillForegroundGapsStep() foregroundThreshold="+foregroundThreshold+" maxDifference="+maxDifference+" tiles="+tiles); final Thread[] threads = newThreadArray(threadsMax); final AtomicInteger tileIndexAtomic = new AtomicInteger(0); if (showProgress) IJ.showProgress(0.0); if (showProgress) IJ.showStatus("Setting up zMap for image "+nImg+" ("+(nImg+1)+" of "+this.disparityScales.length+") ..."); final AtomicInteger numberUpdated = new AtomicInteger(0); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { for (int tile=tileIndexAtomic.getAndIncrement(); tile4) System.out.println("0:fillForegroundTile("+tileX+","+tileY+",zMap,"+minNeib+","+maxDifference+","+foregroundThreshold+","+debugLevel+"), zTile.foregroundIndex="+zTile.foregroundIndex); if (zTile.foregroundIndex==0) return false; // already front if (debugLevel>3) System.out.println("fillForegroundTile("+tileX+","+tileY+",zMap,"+minNeib+","+maxDifference+","+foregroundThreshold+","+debugLevel+"), zTile.foregroundIndex="+zTile.foregroundIndex); int [][] dirs8={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; for (int plane=0; plane =foregroundThreshold){ // See if there are enough neighbors where current foreground is close enough to this one int numMatched=0; for (int iDir=0;iDir=zMap.length){ // System.out.print("fillForegroundTile("+tileX+","+tileX+",...) zMap.length="+zMap.length+" zMap[0].length="+zMap[0].length); // } if ((tileY1>=0) && (tileY1=0) && (tileX1=minNeib){ zTile.setForegroundPlane(plane); return true; } } return false; } public void foregroundByOcclusion( double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? int imageFullWidth, double bgFraction, // process maximal correlation or the first farther plane with correlation intensity >= this part of maximal double blurVarianceSigma, int refineTilePeriod, double refinePhaseCoeff, double refineHighPassSigma, double refineLowPassSigma, double refineCorrMaxDistance, double refineCorrThreshold, int refineSubPixel, double zMapMinForeground, int zMapVarMask, double [] zMapVarThresholds, double [] zMapVarWeights, int normalizeCorrelation, int zMapCorrMask, double [] zMapCorrThresholds, double [] zMapCorrWeights, int debugRow, int debugColumn, int threadsMax, boolean showProgress, int debugLevel){ // process all image pairs for (int nImg=0;nImg= this part of maximal blurVarianceSigma, refineTilePeriod, refinePhaseCoeff, refineHighPassSigma, refineLowPassSigma, refineCorrMaxDistance, refineCorrThreshold, refineSubPixel, zMapMinForeground, zMapVarMask, zMapVarThresholds, zMapVarWeights, normalizeCorrelation, zMapCorrMask, zMapCorrThresholds, zMapCorrWeights, debugRow, debugColumn, threadsMax, showProgress, debugLevel); } } public void foregroundByOcclusion( // remove unneeded parameters later final int nImg, final int[] sImg, // list of second images final double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? final int imageFullWidth, final double bgFraction, // process maximal correlation or the first farther plane with correlation intensity >= this part of maximal final double blurVarianceSigma, final int refineTilePeriod, final double refinePhaseCoeff, final double refineHighPassSigma, final double refineLowPassSigma, final double refineCorrMaxDistance, final double refineCorrThreshold, final int refineSubPixel, final double zMapMinForeground, final int zMapVarMask, final double [] zMapVarThresholds, final double [] zMapVarWeights, final int normalizeCorrelation, final int zMapCorrMask, final double [] zMapCorrThresholds, final double [] zMapCorrWeights, final int debugRow, final int debugColumn, final int threadsMax, final boolean showProgress, final int debugLevel){ final int debugThreshold=2; final Rectangle zMapWOI=this.zMapWOI; final ZTile [][] thisZMap=this.zMap[nImg]; final int tilesX=this.zMapWOI.width; final int tiles=this.zMapWOI.width*this.zMapWOI.height; if (debugLevel>2) System.out.println("setupZMap() zMapWOI.x="+zMapWOI.x+" zMapWOI.y="+zMapWOI.y+ " zMapWOI.width="+zMapWOI.width+" zMapWOI.height="+zMapWOI.height); final Thread[] threads = newThreadArray(threadsMax); final AtomicInteger tileIndexAtomic = new AtomicInteger(0); if (showProgress) IJ.showProgress(0.0); if (showProgress) IJ.showStatus("Filtering zMap foreground for image "+nImg+" ("+(nImg+1)+" of "+this.disparityScales.length+") ..."); final int debugTile=debugRow*tilesX+debugColumn; //TODO: define here final double [] window=new double [4*this.overlapStep*this.overlapStep]; window[0]=Double.NaN; final double [] refineWindow=new double [refineTilePeriod*refineTilePeriod*4]; refineWindow[0]=Double.NaN; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { DoubleFHT doubleFHT = new DoubleFHT(); DoubleFHT refineFHT= new DoubleFHT(); for (int tile=tileIndexAtomic.getAndIncrement(); tile=debugThreshold) && (tile==debugTile); int thisDebugLevel=debugLevel+(debugThis?2:0); // while (! foregroundByOcclusionTile ( zMapWOI.x+tile%tilesX, //int tileX, zMapWOI.y+tile/tilesX, //int tileY, thisZMap, nImg, sImg, // list of second images bgFraction, // process maximal correlation or the first farther plane with correlation intensity >= this part of maximal imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? imageFullWidth, blurVarianceSigma, refineTilePeriod, refinePhaseCoeff, refineHighPassSigma, refineLowPassSigma, refineCorrMaxDistance, refineCorrThreshold, refineSubPixel, zMapMinForeground, zMapVarMask, zMapVarThresholds, zMapVarWeights, normalizeCorrelation, zMapCorrMask, zMapCorrThresholds, zMapCorrWeights, window, doubleFHT, refineWindow, refineFHT, threadsMax, showProgress, thisDebugLevel); //); if (showProgress){ final int finalTile=tile; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { IJ.showProgress(finalTile,tiles); } }); } } } }; } startAndJoin(threads); IJ.showProgress(1.0); } private void foregroundByOcclusionTile ( // false if nothing left in the current foreground, may repeat int tileX, int tileY, ZTile [][] thisZMap, int nImg, int [] sImgSet, // list of second images double bgFraction, // process maximal correlation or the first farther plane with correlation intensity >= this part of maximal double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? int imageFullWidth, double blurVarianceSigma, int refineTilePeriod, double refinePhaseCoeff, double refineHighPassSigma, double refineLowPassSigma, double refineCorrMaxDistance, double refineCorrThreshold, int refineSubPixel, double zMapMinForeground, int zMapVarMask, double [] zMapVarThresholds, double [] zMapVarWeights, int normalizeCorrelation, int zMapCorrMask, double [] zMapCorrThresholds, double [] zMapCorrWeights, double [] window, DoubleFHT doubleFHT, double [] refineWindow, DoubleFHT refineFHT, int threadsMax, boolean showProgress, int debugLevel){ double [] normVarWeights= normalizeWeights(zMapVarMask,zMapVarWeights); double [] normCorrWeights=normalizeWeights(zMapCorrMask,zMapCorrWeights); /// final double [][] disparityScales=this.disparityScales; // this filter will iterate through second images and AND filter for foreground pixels only ZTile zTile=thisZMap[tileY][tileX]; if (zTile==null) return; // true; // tile is not defined, nothing left to do // find the background plane to process int bgPlane=0; if (zTile.maximums.length>0){ for (int i=0;izTile.maximums[bgPlane][1]) bgPlane=i; if (bgFraction>0){ int bgPlane1=bgPlane+1; for (int i=bgPlane+1;izTile.maximums[bgPlane1][1]) bgPlane1=i; if ((bgPlane1=bgFraction*zTile.maximums[bgPlane][1])) bgPlane=bgPlane1; } else if (bgFraction<0){ // compare with foreground boolean hasFG=false; for (int i=0;i=(-bgFraction)*zTile.maximums[bgPlane][1]){ hasFG=true; break; } if (!hasFG){ bgPlane++; if (bgPlanezTile.maximums[bgPlane][1]) bgPlane=i; } } } else { // use gap-fileld FG //zTile.foregroundIndex if (zTile.foregroundIndex>=bgPlane){ bgPlane++; if (bgPlanezTile.maximums[bgPlane][1]) bgPlane=i; } } } } // now bgPlane - background plane (if >=maximums.length - use infinity) // double bgFraction, // process maximal correlation or the first farther plane with correlation intensity >= this part of maximal // is there any forground? /* if (!zTile.advanceForeground()) return true; // got to the end if (zTile.enabledPixels[zTile.foregroundIndex]==null) { zTile.initEnabledForegroundPixelsPlane(); } */ int tileOverlap=zTile.getOverlap(); int paddedSize=zTile.getPaddedSize(); int paddedLength=zTile.getPaddedLength(); // double disparity=zTile.maximums[zTile.foregroundIndex][0]; double disparity=(bgPlane>=zTile.maximums.length)?0.0:zTile.maximums[bgPlane][0]; int size=this.overlapStep*2; int margin=size/4-tileOverlap; int [] dirs1={1,size+1,size,size-1,-1,-size-1,-size,-size+1,0}; double [][][][] slices=new double [sImgSet.length][][][]; double [][][][] variance=new double [sImgSet.length][][][]; // [sIndex][pair 0/1][chn][pixel int length=0; // initialize it to a correct value right here? // int numOther=sImgSet.length; int [][] otherPairs=new int [(sImgSet.length*(sImgSet.length-1))/2][2]; // with 3 images there is only one other pair, but try to maki it work later with more images { int index=0; for (int i=0;i4){ (new ShowDoubleFloatArrays()).showArrays( borderMask, size, size, "borderMask"); } // zTile.initAux(2); // zTile.initAux(7); zTile.initAux(11); if (debugLevel>3){ // +2 for selected tile System.out.println("otherPairs.length="+otherPairs.length+ ""); for (int i=0;i4)&& (n==0) && (chn==0) && (numPix!=9) ){ System.out.println (" x="+(i%size)+" y="+(i/size)+" i="+i+" numPix="+numPix+" ="+borderMask[i]+" v2="+v2); } // v2+=(S2-(S1*S1)/numPix)/numPix; //dirs1.length; variance[sIndex][n][chn][i]=Math.sqrt(v2); //java.lang.NullPointerException } if (blurVarianceSigma>0.0){ if (debugLevel>3) // +2 for selected tile System.out.println("Bluring varaince["+sIndex+"]["+n+"]["+chn+"] with sigma="+blurVarianceSigma); (new DoubleGaussianBlur()).blurDouble( variance[sIndex][n][chn], size, size, blurVarianceSigma, blurVarianceSigma, 0.01); } } } if (debugLevel>3){ // +2 for selected tile int numActive=0; for (int i=0;i4) { System.out.print("chn="+chn+" nPair="+nPair+" i="+i+" iPix="+iPix+ " ix="+(i%paddedSize)+" iy="+(i/paddedSize)+ " ipx="+(iPix%size)+" ipy="+(iPix/size)+ " v["+sIndex0+"][0]["+chn+"]["+iPix+"]="+IJ.d2s(variance[sIndex0][0][chn][iPix],5)+ " v["+sIndex1+"][0]["+chn+"]["+iPix+"]="+IJ.d2s(variance[sIndex1][0][chn][iPix],5)+ " v["+sIndex0+"][1]["+chn+"]["+iPix+"]="+IJ.d2s(variance[sIndex0][1][chn][iPix],5)+ " v["+sIndex1+"][1]["+chn+"]["+iPix+"]="+IJ.d2s(variance[sIndex1][1][chn][iPix],5)+ " pairVar="+IJ.d2s(pairVar,5)); } double pairDiff=Math.abs(slices[sIndex0][chn+1][1][iPix]-slices[sIndex1][chn+1][1][iPix])/pairVar/zMapVarThresholds[chn]; double thisDiff=Math.abs(0.5*( slices[sIndex0][chn+1][0][iPix]+ slices[sIndex1][chn+1][0][iPix]- slices[sIndex0][chn+1][1][iPix]- slices[sIndex1][chn+1][1][iPix])); occlVal+=normVarWeights[chn]*(thisDiff/pairVar/zMapVarThresholds[chn])/(pairDiff+k); // consider both - with /pairVar and without pairVarDbg+=normVarWeights[chn]*pairVar; pairDiffDbg+=normVarWeights[chn]*pairDiff; thisDiffDbg+=normVarWeights[chn]*thisDiff; double dd=slices[sIndex0][chn+1][1][iPix]-slices[sIndex1][chn+1][1][iPix]; thisDiffVar+=normVarWeights[chn]*Math.sqrt(0.5*(dd*dd+pairVar*pairVar)); // other variant - if pairDiff>1.0 - => occlVal=0.0; } if ((nPair==0) || (occlVarVal4) { System.out.println( " pairVarDbgBest="+IJ.d2s(pairVarDbgBest,5)); } } aux0[i]=(float) occlVarVal; // debug // aux2[i]=(float) disparity; aux3[i]=(float) pairVarDbgBest; aux4[i]=(float) pairDiffDbgBest; aux5[i]=(float) thisDiffDbgBest; aux6[i]=(float) thisDiffVarBest; } zTile.setAux(0,aux0); // debug // zTile.setAux(2,aux2); zTile.setAux(3,aux3); zTile.setAux(4,aux4); zTile.setAux(5,aux5); zTile.setAux(6,aux6); if (debugLevel>3){ float [][] debugData={aux2,aux3,aux4,aux5,aux6,aux0}; String [] debugTitles={"var","pair","this","occl","dvar","disp"}; (new ShowDoubleFloatArrays()).showArrays( debugData, paddedSize, paddedSize, true, "R"+nImg+"_tile"+tileX+"-"+tileY, debugTitles); } } if (zMapCorrMask!=0) { // 8x8 correlation to find occlusion double [][][]refineSmoothThis= new double [sImgSet.length][][]; double [][][]refineSmoothOther=new double [otherPairs.length][][]; for (int sIndex=0;sIndex3)?("RST"+nImg+"-"+sImg+"_"+chn):null); } } for (int nPair=0;nPair3)?("RS-"+nImg+"-"+sImg0+"-"+sImg1+"_"+chn):null); } } float [] aux1=new float[paddedLength]; // float [] aux7=new float[paddedLength]; float [] aux8=new float[paddedLength]; float [] aux9=new float[paddedLength]; float [] aux10=new float[paddedLength]; for (int i=0;ibestOcclCorrVal)) { bestOcclCorrVal=thisOcclCorrVal; bestOcclCorrOther=refineSmoothOther[nPair][chn][iPix]; bestOcclCorrFirst=refineSmoothThis[sIndex0][chn][iPix]; bestOcclCorrSecond=refineSmoothThis[sIndex1][chn][iPix]; // bestPair=nPair; } } occlCorrVal+=normCorrWeights[chn]*bestOcclCorrVal; occlCorrOther+=normCorrWeights[chn]*bestOcclCorrOther; occlCorrFirst+=normCorrWeights[chn]*bestOcclCorrFirst; occlCorrSecond+=normCorrWeights[chn]*bestOcclCorrSecond; } // Save result to aux[1] aux1[i]=(float) occlCorrVal; aux7[i]=(float) occlCorrOther; aux8[i]=(float) (occlCorrFirst+occlCorrSecond)/2; aux9[i]=(float) occlCorrFirst; aux10[i]=(float) occlCorrSecond; } zTile.setAux(1,aux1); zTile.setAux(7,aux7); zTile.setAux(8,aux8); zTile.setAux(9,aux9); zTile.setAux(10,aux10); if (debugLevel>3){ float [][] debugData={aux1,aux7,aux8,aux9,aux10}; String [] debugTitles={"rslt","other","average","first","second"}; (new ShowDoubleFloatArrays()).showArrays( debugData, paddedSize, paddedSize, true, "C"+nImg+"_tile"+tileX+"-"+tileY, debugTitles); } } return; // zTile.isForegroundValid(); // this current plane is not empty } public double [] normalizeWeights(int mask,double [] weights){ double [] maskedWeights=weights.clone(); for (int i=0;i2) System.out.println("setupZMap() zMapWOI.x="+zMapWOI.x+" zMapWOI.y="+zMapWOI.y+ " zMapWOI.width="+zMapWOI.width+" zMapWOI.height="+zMapWOI.height); final Thread[] threads = newThreadArray(threadsMax); final AtomicInteger tileIndexAtomic = new AtomicInteger(0); if (showProgress) IJ.showProgress(0.0); if (showProgress) IJ.showStatus("Filtering zMap foreground for image "+nImg+" ("+(nImg+1)+" of "+this.disparityScales.length+") ..."); final int debugTile=debugRow*tilesX+debugColumn; //TODO: define here final double [] window=new double [4*this.overlapStep*this.overlapStep]; window[0]=Double.NaN; final double [] refineWindow=new double [refineTilePeriod*refineTilePeriod*4]; refineWindow[0]=Double.NaN; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { DoubleFHT doubleFHT = new DoubleFHT(); DoubleFHT refineFHT= new DoubleFHT(); for (int tile=tileIndexAtomic.getAndIncrement(); tile=debugThreshold) && (tile==debugTile); int thisDebugLevel=debugLevel+(debugThis?2:0); while (!filterForegroundZMapTile ( zMapWOI.x+tile%tilesX, //int tileX, zMapWOI.y+tile/tilesX, //int tileY, thisZMap, nImg, sImg, // list of second images imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? imageFullWidth, blurVarianceSigma, refineTilePeriod, refinePhaseCoeff, refineHighPassSigma, refineLowPassSigma, refineCorrMaxDistance, refineCorrThreshold, refineSubPixel, zMapMinForeground, zMapVarMask, zMapVarThresholds, zMapVarWeights, auxVarMode, normalizeCorrelation, zMapCorrMask, zMapCorrThresholds, zMapCorrWeights, window, doubleFHT, refineWindow, refineFHT, threadsMax, showProgress, thisDebugLevel)); if (showProgress){ final int finalTile=tile; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { IJ.showProgress(finalTile,tiles); } }); } } } }; } startAndJoin(threads); IJ.showProgress(1.0); } private boolean filterForegroundZMapTile ( // false if nothing left in the current foreground, may repeat int tileX, int tileY, ZTile [][] thisZMap, int nImg, int [] sImgSet, // list of second images double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? int imageFullWidth, double blurVarianceSigma, int refineTilePeriod, double refinePhaseCoeff, double refineHighPassSigma, double refineLowPassSigma, double refineCorrMaxDistance, double refineCorrThreshold, int refineSubPixel, double zMapMinForeground, int zMapVarMask, double [] zMapVarThresholds, double [] zMapVarWeights, int auxVarMode, int normalize, int zMapCorrMask, double [] zMapCorrThresholds, double [] zMapCorrWeights, double [] window, DoubleFHT doubleFHT, double [] refineWindow, DoubleFHT refineFHT, int threadsMax, boolean showProgress, int debugLevel){ // double [] normVarWeights= normalizeWeights(zMapVarMask,zMapVarWeights); // double [] normCorrWeights=normalizeWeights(zMapCorrMask,zMapCorrWeights); int auxChannelNumber=3; /// final double [][] disparityScales=this.disparityScales; // this filter will iterate through second images and AND filter for foreground pixels only ZTile zTile=thisZMap[tileY][tileX]; if (zTile==null) return true; // tile is not defined, nothing left to do // is there any forground? if (!zTile.advanceForeground()) return true; // got to the end if (zTile.enabledPixels[zTile.foregroundIndex]==null) { zTile.initEnabledForegroundPixelsPlane(); } int tileOverlap=zTile.getOverlap(); int paddedSize=zTile.getPaddedSize(); int paddedLength=zTile.getPaddedLength(); double disparity=zTile.maximums[zTile.foregroundIndex][0]; int size=this.overlapStep*2; int margin=size/4-tileOverlap; int [] dirs1={1,size+1,size,size-1,-1,-size-1,-size,-size+1,0}; for (int sIndex=0;sIndex3){ // +2 for selected tile System.out.println("Debugging tileX="+tileX+", tileY="+tileY+" disparity="+disparity+" dX="+ dX+" dY="+dY); int numActive=0; for (int i=0;i=0; df=Math.abs(df); if ((d==0) || (df0.0){ (new DoubleGaussianBlur()) .blurDouble( variance, size, size, blurVarianceSigma, blurVarianceSigma, 0.01); } for (int i=0;i=bDiff.length) || (i1>=variance.length)){ System.out.println("\nfilterForegroundZMapTile() i="+i+" margin="+margin+" paddedSize="+paddedSize+" bDiff.length="+bDiff.length+ " i1="+i1+" size="+size+" variance.length="+variance.length); } if ((bDiff[i1]/variance[i1])>zMapVarThresholds[chn]) { // 1024 zTile.disableForegroundPixel(i); } } if (debugLevel>3){ // +2 for selected tile String [] channelNames={"alpha","Y","Cb","Cr","Aux"}; String [] debugTitles={"bDiff","var","bdif/var","good","cumul"}; double [][] debugData=new double[5][length]; debugData[0]=bDiff; debugData[1]=variance; for (int i=0;izMapVarThresholds[chn])?-1.0:1.0; debugData[4][i]=zTile.isEnabledForegroundPixel(i)?-1.0:1.0; } (new ShowDoubleFloatArrays()).showArrays( debugData, size, size, true, "diff_"+channelNames[chn+1]+"_"+tileX+"-"+tileY+"_"+nImg+"-"+sImg, debugTitles); } } else { // aux channel for (int i=0;i3){ // +2 for selected tile (new ShowDoubleFloatArrays()).showArrays( bDiff, size, size, "AUX_"+nImg+"-"+sImg+"__"+tileX+"-"+tileY); } for (int i=0;i3)?("CORR"+nImg+"-"+sImg+"_"+chn):null); for (int i=0;i2) System.out.println("setupZMap() zMapWOI.x="+zMapWOI.x+" zMapWOI.y="+zMapWOI.y+ " zMapWOI.width="+zMapWOI.width+" zMapWOI.height="+zMapWOI.height); final Thread[] threads = newThreadArray(threadsMax); final AtomicInteger tileIndexAtomic = new AtomicInteger(0); if (showProgress) IJ.showProgress(0.0); if (showProgress) IJ.showStatus("refinePlaneDisparity for image "+nImg+" ("+(nImg+1)+" of "+this.disparityScales.length+") ..."); final int debugTile=debugRow*tilesX+debugColumn; //TODO: define here // final double [] window=new double [4*this.overlapStep*this.overlapStep]; // window[0]=Double.NaN; // final double [] refineWindow=new double [refineTilePeriod*refineTilePeriod*4]; // refineWindow[0]=Double.NaN; final int size=2*this.overlapStep; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { DoubleFHT doubleFHT = new DoubleFHT(); final double [] window=new double [size*size]; window[0]=Double.NaN; // DoubleFHT refineFHT= new DoubleFHT(); for (int tile=tileIndexAtomic.getAndIncrement(); tile=debugThreshold) && (tile==debugTile); int thisDebugLevel=debugLevel+(debugThis?2:0); refinePlaneDisparityTile ( zMapWOI.x+tile%tilesX, //int tileX, zMapWOI.y+tile/tilesX, //int tileY, zMapFinal, nImg, sImg, // list of second images imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? imageFullWidth, // blurVarianceSigma, // refineTilePeriod, refinePhaseCoeff, refineHighPassSigma, refineLowPassSigma, refineCorrMaxDistance, refineCorrThreshold, refineSubPixel, // zMapMinForeground, // zMapVarMask, // zMapVarThresholds, // zMapVarWeights, // auxVarMode, // normalizeCorrelation, zMapCorrMask, zMapCorrThresholds, zMapCorrWeights, window, doubleFHT, // refineWindow, // refineFHT, disparityMax, disparityMin, minAbsolute, // or NaN - will use enabled/disabled state of the tile minRelative, filterByForeground, // apply known certain masks filterByForegroundMargin, filterByDisabled, disparityTolearnce, maskBlurSigma, corrHighPassSigma, // subtract blurred version to minimize correlation caused by masks threadsMax, showProgress, thisDebugLevel); if (showProgress){ final int finalTile=tile; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { IJ.showProgress(finalTile,tiles); } }); } } } }; } startAndJoin(threads); IJ.showProgress(1.0); } private void refinePlaneDisparityTile ( // false if nothing left in the current foreground, may repeat int tileX, int tileY, ZTile [][][] allZMap, int nImg, int [] sImgSet, // list of second images double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? int imageFullWidth, // double blurVarianceSigma, // int refineTilePeriod, double refinePhaseCoeff, double refineHighPassSigma, double refineLowPassSigma, double refineCorrMaxDistance, double refineCorrThreshold, int refineSubPixel, // double zMapMinForeground, // int zMapVarMask, // double [] zMapVarThresholds, // double [] zMapVarWeights, // int auxVarMode, // int normalize, int zMapCorrMask, double [] zMapCorrThresholds, double [] zMapCorrWeights, double [] window, DoubleFHT doubleFHT, // double [] refineWindow, // DoubleFHT refineFHT, // new arguments // int combineMode, // different image pairs - 0 double disparityMax, double disparityMin, double minAbsolute, // or NaN - will use enabled/disabled state of the tile double minRelative, boolean filterByForeground, // apply known certain masks double filterByForegroundMargin, boolean filterByDisabled, double disparityTolearnce, double maskBlurSigma, double corrHighPassSigma, // subtract blurred version to minimize correlation caused by masks int threadsMax, boolean showProgress, int debugLevel){ // TODO: also calculate "unlikely" - high autocorrelation, not occluded, low inter-correlation double [] normCorrWeights=normalizeWeights(zMapCorrMask,zMapCorrWeights); ZTile zTile=allZMap[nImg][tileY][tileX]; int tileOverlap=zTile.getOverlap(); int paddedSize=zTile.getPaddedSize(); int paddedLength=zTile.getPaddedLength(); int size=this.overlapStep*2; int length=size*size; double[] zeros=new double [length]; for (int i=0;i3) { System.out.println ("zTile.setMinCorrelations("+minAbsolute+","+ minRelative+")"); boolean [] enabled=zTile.enabledPlane; for (int plane=0;plane3) { System.out.println ("refinePlaneDisparityTile("+tileX+","+tileY+", ...) plane="+plane+ " disparity="+zTile.getPlaneDisparity(plane)+" strength="+zTile.getPlaneStrength(plane)); } if (!(zTile.getPlaneDisparity(plane)disparityMax)){ // NaN is OK double disparity= zTile.getPlaneDisparity(plane); if (debugLevel>3) { // double testDisparityError=0.5; System.out.println (" processing plane "+plane+" nImg="+nImg+ " disparity="+disparity+ " filterByForegroundMargin="+filterByForegroundMargin); // disparity+=testDisparityError; // System.out.println ("**** modified disparity for testing, new disparity="+disparity); } double [] thisEnabledMask; if (filterByForeground){ boolean [] bMask=zTile.getEnabledNonOccluded( disparity+filterByForegroundMargin, disparity, filterByDisabled?disparityTolearnce:Double.NaN, debugLevel); if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( bMask, paddedSize, paddedSize, "bMask_N"+nImg+"-X"+tileX+"-Y"+tileY ); } thisEnabledMask=zeros.clone(); for (int i=0;i0.0){ (new DoubleGaussianBlur()).blurDouble( thisEnabledMask, size, size, maskBlurSigma, maskBlurSigma, 0.01); } if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( thisEnabledMask, size, size, "thisEnabledMask_N"+nImg+"-X"+tileX+"-Y"+tileY ); } } else { thisEnabledMask=null; } double [][] pairEnabledMask=new double [sImgSet.length][]; double [] disparityArray=null; // int disparityRange=(int) Math.ceil(refineCorrMaxDistance*refineSubPixel*2); for (int sIndex=0;sIndex3){ // +2 for selected tile System.out.println("Iterating tileX="+tileX+", tileY="+tileY+" disparity="+disparity+" dX="+ dX+" dY="+dY+ " sIndex="+sIndex+" nImg="+nImg+" sImg="+sImg); } if (filterByForeground){ double [] otherNonOccluded= getNonOccluded( allZMap[sImg],//ZTile [][] thisZMap, tileX*this.overlapStep+this.overlapStep/2+dX, //double xc, tileY*this.overlapStep+this.overlapStep/2+dY, //double yc, disparity, //double disparity, filterByForegroundMargin, filterByDisabled?disparityTolearnce:Double.NaN, debugLevel); if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( otherNonOccluded, paddedSize, paddedSize, "PEM_N"+nImg+"-S"+sImg+"-X"+tileX+"-Y"+tileY ); } pairEnabledMask[sIndex]=zeros.clone(); for (int i=0;i0.0){ (new DoubleGaussianBlur()).blurDouble( pairEnabledMask[sIndex], size, size, maskBlurSigma, maskBlurSigma, 0.01); } if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( pairEnabledMask[sIndex], size, size, "pairEnabledMask_N"+nImg+"-S"+sImg+"-X"+tileX+"-Y"+tileY ); } //multiply masks from both images for (int i=0;i3){ // +2 for selected tile System.out.println("Debugging tileX="+tileX+", tileY="+tileY+" plane="+plane+" disparity="+disparity+" dX="+ dX+" dY="+dY+ " sIndex="+sIndex+" nImg="+nImg+" sImg="+sImg); int numActive=0; for (int i=0;i0.0){ double [] loPass=pairs[chn][n].clone(); (new DoubleGaussianBlur()).blurDouble( loPass, size, size, corrHighPassSigma, corrHighPassSigma, 0.01); for (int i=0;i3){ // +2 for selected tile System.out.println("centerCorr[0]="+centerCorr[0]+" centerCorr[1]="+centerCorr[1]+" centerCorr[2]="+centerCorr[2]); // show high-pass/masked pairs int numActive=0; for (int i=0;i=debugData.length) || ((i-1)>=pairs.length)){ System.out.println( " index="+index+ " debugData.length="+debugData.length+ " i="+i+ " pairs.length="+pairs.length+ " slices.length="+slices.length+ " slices[sIndex].length="+slices[sIndex].length ); } debugData[index++]=pairs[i-1][0]; //ava.lang.ArrayIndexOutOfBoundsException: 3 debugTitles[index]=channelNames[i]+"-"+sImg; debugData[index++]=pairs[i-1][1]; } (new ShowDoubleFloatArrays()).showArrays( debugData, size, size, true, "P"+plane+"_N"+nImg+"-"+sImg+"_sIndex-"+sIndex+"_tile"+tileX+"-"+tileY, debugTitles); } // TODO: now look if the correlation is strong enough to perform correction (minimal of 3?) double minCorr=1.0; for (int i=0;i3){ // +2 for selected tile System.out.println("centerCorr[0]="+centerCorr[0]+" centerCorr[1]="+centerCorr[1]+" centerCorr[2]="+centerCorr[2]+ " minCorr="+minCorr+" refineCorrThreshold="+refineCorrThreshold); } if (minCorr>=refineCorrThreshold){ double [] combinedChnCorr=new double [length]; for (int i=0;imax){ max=combinedChnCorr[i]; iMax=i; } int ixc=iMax%size-size/2; int iyc=iMax/size-size/2; if (debugLevel>3) System.out.println("refineCorrelation(): max="+max+" iMax="+iMax+" refineCorrMaxDistance="+refineCorrMaxDistance+" corrMaxDist2="+corrMaxDist2+ " r2="+(ixc*ixc+iyc*iyc)+" r="+Math.sqrt(ixc*ixc+iyc*iyc)); if ((ixc*ixc+iyc*iyc)<=corrMaxDist2){ // maximum close enough double [] upsampled=doubleFHT.upsample(combinedChnCorr,refineSubPixel); int interpolatedSize=size*refineSubPixel; int interpolatedCenter=(interpolatedSize+1)*interpolatedSize/2; if (debugLevel>3) { (new ShowDoubleFloatArrays()).showArrays( upsampled, interpolatedSize, interpolatedSize, "U"+plane+"_N"+nImg+"-"+sImg+"_sIndex-"+sIndex+"_tile"+tileX+"-"+tileY); } if (disparityArray==null) { disparityArray=new double [2*halfDispRange+1]; for (int i=0;i5){ System.out.println("disparityArray["+i+"]="+disparityArray[i]+ " deltaX="+deltaX+" deltaY="+deltaY+" iX="+iX+" iY="+iY+" index00="+index00+" index01="+index01+ " index10="+index10+" index11="+index11); } } } } // if (minCorr>=refineCorrThreshold) } //for (int sIndex=0;sIndexdisparityArray[iMax]) iMax=i; } double disparCorr=((double) (iMax-halfDispRange))/refineSubPixel; if (debugLevel>5){ for (int i=0;i3){ System.out.println("Old disparity["+plane+"]="+disparity+" new disparity="+(disparity+disparCorr)); } zTile.setPlaneDisparity(disparity+disparCorr,plane); } } } } // end for (int plane...) } public void planeLikely( // int nImg, // int [] sImg, // list of second images double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? int imageFullWidth, double blurVarianceSigma, int refineTilePeriod, double subTilePhaseCoeff, double subTileHighPassSigma, double subTileLowPassSigma, // double refineCorrMaxDistance, // double refineCorrThreshold, int refineSubPixel, double zMapMinForeground, int zMapVarMask, double [] zMapVarThresholds, double [] zMapVarWeights, int auxVarMode, int normalizeCorrelation, int zMapCorrMask, double [] zMapCorrThresholds, double [] zMapCorrThresholdsRel, double [] zMapCorrWeights, int debugRow, int debugColumn, int combineMode, // different image pairs - 0 double disparityMax, double disparityMin, double minAbsolute, // or NaN - will use enabled/disabled state of the tile double minRelative, boolean filterByForeground, // apply known certain masks double filterByForegroundMargin, boolean filterByDisabled, double disparityTolearnce, double maskBlurSigma, double corrHighPassSigma, // subtract blurred version to minimize correlation caused by masks double varianceBlurScale, double kLocal, int matchStatMode, int threadsMax, boolean showProgress, int debugLevel){ // process all image pairs for (int nImg=0;nImg2) System.out.println("setupZMap() zMapWOI.x="+zMapWOI.x+" zMapWOI.y="+zMapWOI.y+ " zMapWOI.width="+zMapWOI.width+" zMapWOI.height="+zMapWOI.height); final Thread[] threads = newThreadArray(threadsMax); final AtomicInteger tileIndexAtomic = new AtomicInteger(0); if (showProgress) IJ.showProgress(0.0); if (showProgress) IJ.showStatus("planeLikely for image "+nImg+" ("+(nImg+1)+" of "+this.disparityScales.length+") ..."); final int debugTile=debugRow*tilesX+debugColumn; //TODO: define here // final double [] window=new double [4*this.overlapStep*this.overlapStep]; // window[0]=Double.NaN; // final double [] refineWindow=new double [refineTilePeriod*refineTilePeriod*4]; // refineWindow[0]=Double.NaN; final int size=2*this.overlapStep; for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { @Override public void run() { DoubleFHT doubleFHT = new DoubleFHT(); final double [] window=new double [size*size]; window[0]=Double.NaN; final double [] subTileWindow=new double [refineTilePeriod*refineTilePeriod*4]; subTileWindow[0]=Double.NaN; DoubleFHT subTileFHT= new DoubleFHT(); for (int tile=tileIndexAtomic.getAndIncrement(); tile=debugThreshold) && (tile==debugTile); int thisDebugLevel=debugLevel+(debugThis?2:0); planeLikelyTile ( zMapWOI.x+tile%tilesX, //int tileX, zMapWOI.y+tile/tilesX, //int tileY, zMapFinal, nImg, sImg, // list of second images imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? imageFullWidth, blurVarianceSigma, // refineTilePeriod, subTilePhaseCoeff, subTileHighPassSigma, subTileLowPassSigma, // refineCorrMaxDistance, // refineCorrThreshold, refineSubPixel, zMapMinForeground, zMapVarMask, zMapVarThresholds, zMapVarWeights, auxVarMode, normalizeCorrelation, zMapCorrMask, zMapCorrThresholds, zMapCorrThresholdsRel, zMapCorrWeights, window, doubleFHT, subTileWindow, subTileFHT, combineMode, // different image pairs - 0 disparityMax, disparityMin, minAbsolute, // or NaN - will use enabled/disabled state of the tile minRelative, filterByForeground, // apply known certain masks filterByForegroundMargin, filterByDisabled, disparityTolearnce, maskBlurSigma, corrHighPassSigma, // subtract blurred version to minimize correlation caused by masks varianceBlurScale, kLocal, matchStatMode, threadsMax, showProgress, thisDebugLevel); if (showProgress){ final int finalTile=tile; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { IJ.showProgress(finalTile,tiles); } }); } } } }; } startAndJoin(threads); IJ.showProgress(1.0); } private void planeLikelyTile ( // false if nothing left in the current foreground, may repeat int tileX, int tileY, ZTile [][][] allZMap, int nImg, int [] sImgSet, // list of second images double [][][] imageData, // [img]{Alpha, Y,Cb,Cr, Ext}. Alpha may be null - will handle later? int imageFullWidth, double blurVarianceSigma, // int refineTilePeriod, double subTilePhaseCoeff, double subTileHighPassSigma, double subTileLowPassSigma, // double refineCorrMaxDistance, // double refineCorrThreshold, int refineSubPixel, double zMapMinForeground, int zMapVarMask, double [] zMapVarThresholds, double [] zMapVarWeights, int auxVarMode, int normalize, int zMapCorrMask, double [] zMapCorrThresholds, double [] zMapCorrThresholdsRel, double [] zMapCorrWeights, double [] window, DoubleFHT doubleFHT, double [] subTileWindow, DoubleFHT subTileFHT, // new arguments int combineMode, // different image pairs - 0 double disparityMax, double disparityMin, double minAbsolute, // or NaN - will use enabled/disabled state of the tile double minRelative, boolean filterByForeground, // apply known certain masks double filterByForegroundMargin, boolean filterByDisabled, double disparityTolearnce, double maskBlurSigma, double corrHighPassSigma, // subtract blurred version to minimize correlation caused by masks //tone-matching statistical parameters double varianceBlurScale, double kLocal, int matchStatMode, int threadsMax, boolean showProgress, int debugLevel){ // TODO: also calculate "unlikely" - high autocorrelation, not occluded, low inter-correlation double [] normVarWeights= normalizeWeights(zMapVarMask,zMapVarWeights); double [] normCorrWeights=normalizeWeights(zMapCorrMask,zMapCorrWeights); // int auxChannelNumber=3; ZTile zTile=allZMap[nImg][tileY][tileX]; int tileOverlap=zTile.getOverlap(); int paddedSize=zTile.getPaddedSize(); int paddedLength=zTile.getPaddedLength(); int size=this.overlapStep*2; int length=size*size; double[] zeros=new double [length]; for (int i=0;i3) { System.out.println ("zTile.setMinCorrelations("+minAbsolute+","+ minRelative+")"); boolean [] enabled=zTile.enabledPlane; for (int plane=0;plane3) { System.out.println ("planeLikelyTile("+tileX+","+tileY+", ...) plane="+plane+ " disparity="+zTile.getPlaneDisparity(plane)+" strength="+zTile.getPlaneStrength(plane)); } if (!(zTile.getPlaneDisparity(plane)disparityMax)){ // NaN is OK planeStrength[plane]=new double [sImgSet.length][]; double disparity= zTile.getPlaneDisparity(plane); if (debugLevel>3) { System.out.println (" processing plane "+plane+" nImg="+nImg+ " disparity="+disparity+ " filterByForegroundMargin="+filterByForegroundMargin); } double [] thisEnabledMask; if (filterByForeground){ boolean [] bMask=zTile.getEnabledNonOccluded( disparity+filterByForegroundMargin, disparity, filterByDisabled?disparityTolearnce:Double.NaN, debugLevel); if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( bMask, paddedSize, paddedSize, "bMask_N"+nImg+"-X"+tileX+"-Y"+tileY ); } thisEnabledMask=zeros.clone(); for (int i=0;i0.0){ (new DoubleGaussianBlur()).blurDouble( thisEnabledMask, size, size, maskBlurSigma, maskBlurSigma, 0.01); } if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( thisEnabledMask, size, size, "thisEnabledMask_N"+nImg+"-X"+tileX+"-Y"+tileY ); } } else { thisEnabledMask=null; } double [][] pairEnabledMask=new double [sImgSet.length][]; // double [] disparityArray=null; // needed?? for (int sIndex=0;sIndex3){ // +2 for selected tile System.out.println("Iterating tileX="+tileX+", tileY="+tileY+" disparity="+disparity+" dX="+ dX+" dY="+dY+ " sIndex="+sIndex+" nImg="+nImg+" sImg="+sImg); } if (filterByForeground){ double [] otherNonOccluded= getNonOccluded( allZMap[sImg],//ZTile [][] thisZMap, tileX*this.overlapStep+this.overlapStep/2+dX, //double xc, tileY*this.overlapStep+this.overlapStep/2+dY, //double yc, disparity, //double disparity, filterByForegroundMargin, filterByDisabled?disparityTolearnce:Double.NaN, debugLevel); if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( otherNonOccluded, paddedSize, paddedSize, "PEM_N"+nImg+"-S"+sImg+"-X"+tileX+"-Y"+tileY ); } pairEnabledMask[sIndex]=zeros.clone(); for (int i=0;i0.0){ (new DoubleGaussianBlur()).blurDouble( pairEnabledMask[sIndex], size, size, maskBlurSigma, maskBlurSigma, 0.01); } if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( pairEnabledMask[sIndex], size, size, "pairEnabledMask_N"+nImg+"-S"+sImg+"-X"+tileX+"-Y"+tileY ); } //multiply masks from both images for (int i=0;i3){ // +2 for selected tile System.out.println("Debugging tileX="+tileX+", tileY="+tileY+" plane="+plane+" disparity="+disparity+" dX="+ dX+" dY="+dY+ " sIndex="+sIndex+" nImg="+nImg+" sImg="+sImg); int numActive=0; for (int i=0;i0.0){ double [] loPass=pairs[chn][n].clone(); (new DoubleGaussianBlur()).blurDouble( loPass, size, size, corrHighPassSigma, corrHighPassSigma, 0.01); for (int i=0;i3){ // +2 for selected tile // System.out.println("centerCorr[0]="+centerCorr[0]+" centerCorr[1]="+centerCorr[1]+" centerCorr[2]="+centerCorr[2]); // show high-pass/masked pairs int numActive=0; for (int i=0;i=debugData.length) || ((i-1)>=pairs.length)){ System.out.println( " index="+index+ " debugData.length="+debugData.length+ " i="+i+ " pairs.length="+pairs.length+ " slices.length="+slices.length+ " slices[sIndex].length="+slices[sIndex].length ); } debugData[index++]=pairs[i-1][0]; //ava.lang.ArrayIndexOutOfBoundsException: 3 //Exception in thread "Thread-1033" java.lang.NullPointerException debugTitles[index]=channelNames[i]+"-"+sImg; debugData[index++]=pairs[i-1][1]; } (new ShowDoubleFloatArrays()).showArrays( debugData, size, size, true, "P"+plane+"_N"+nImg+"-"+sImg+"_sIndex-"+sIndex+"_tile"+tileX+"-"+tileY, debugTitles); } //=================== several alternative modes. For likelyhood - find linear relation between both images using weights - proportional to cross-correlation double [] linearMatchWeight=pairEnabledMask[sIndex].clone(); if (corrs!=null){ // and some mode? int tileSize=(int) Math.sqrt(subTileWindow.length); // 8 int tileStep=tileSize/2; //4 int tileExtra=size-tileSize; // 24?? // int tileLength=tileSize*tileSize; // 64 // int tileCenter=(tileSize+1)*tileSize/2; // 36 int topLeft=(size+1)*(size/4-tileSize/4); int numTilesRow=size/(2*tileStep); // 4 // accumulate result double [] corrTile=new double [length]; for (int i=0;i3){ System.out.println ("corrs["+subTileY+"]["+subTileX+"]={"+ corrs[subTileY][subTileX][0]+","+ corrs[subTileY][subTileX][1]+","+ corrs[subTileY][subTileX][2]+"} aCorr="+aCorr+ " corr="+corr); } for (int iy=0;iy0.0)?corrTile[i]:0.0); double sumWindow=0.0; double sumLMW=0.0; for (int i=0;i3){ // +2 for selected tile double [][] debugData={linearMatchWeight,linearMatchWeightNorm}; String [] debugTitles={"LMW","Norm"}; (new ShowDoubleFloatArrays()).showArrays( debugData, size, size, true, "LMW"+plane+"_N"+nImg+"-"+sImg+"_sIndex-"+sIndex+"_tile"+tileX+"-"+tileY, debugTitles); } double [][]staging=new double [slices[sIndex].length-1][]; double [][][]variance=new double[slices[sIndex].length-1][][]; double [] avgVariance=new double[slices[sIndex].length-1]; double [] avgRange= new double[slices[sIndex].length-1]; for (int chn=0;chn5); //double var2){ // second image variance } double [] fVar=new double[2]; for (int n=0;n<2;n++){ fVar[n]=S2tot[n]/S0tot[n]-S1tot[n]*S1tot[n]/S0tot[n]/S0tot[n]; } avgVariance[chn]=Math.sqrt(sumWV2/sumW); avgRange[chn]= Math.sqrt(0.5*(fVar[0]+fVar[1])); if (debugLevel>3){ System.out.println("Average variance for plane "+plane+" nImg="+nImg+" sImg="+sImg+ " chn="+chn+" is "+avgVariance[chn]+" total tile variance="+avgRange[chn]); } // blur stage with scaled variance this.photometric.blurStaging( staging[chn], chn, avgVariance[chn]*varianceBlurScale); //varianceBlurScale }else{ staging[chn]=null; variance[chn]=null; avgVariance[chn]=0.0; avgRange[chn]=0.0; } } double sumRelVar=0.0; for (int chn=0;chn0.0){ if (debugLevel>3){ System.out.println("avgVariance["+chn+"]="+avgVariance[chn]+ " avgRange["+chn+"]="+avgRange[chn]+ " getAverageVariance("+nImg+","+chn+")="+this.photometric.getAverageVariance(nImg,chn)); } // avgVariance[chn]/=this.photometric.getAverageVariance(nImg,chn); // avgVariance[chn]*=avgVariance[chn]; avgRange[chn]/=this.photometric.getAverageVariance(nImg,chn); avgRange[chn]*=avgRange[chn]; sumRelVar+=avgVariance[chn]; } for (int chn=0;chn0.0){ avgVariance[chn]/=sumRelVar; if (debugLevel>3){ System.out.println(" normalized avgVariance["+chn+"]="+avgVariance[chn]); } } if (debugLevel>3){ int numActive=0; for (int i=0;i3) planeStrengthChn=new double [staging.length][length]; for (int chn=0;chn3) { planeStrengthChn[chn]=new double[length]; for (int i=0;i0){ double strength=this.photometric.getStrength( staging[chn], chn, slices[sIndex][chn+1][0][i], //double v1, slices[sIndex][chn+1][1][i]); //double v2 strength*=linearMatchWeight[i]; // remove?? if (window[i]>0.0) strength/=window[i]; planeStrength[plane][sIndex][i]+=avgVariance[chn]*normVarWeights[chn]*strength; if (debugLevel>3) planeStrengthChn[chn][i]=strength; } } else { if (debugLevel>3) planeStrengthChn[chn]=null; } } if (debugLevel>3){ int numActive=0; for (int i=0;i0.0) planeStrengthCombo[plane][i]+=planeStrength[plane][sIndex][i]; } break; case 2: // min for (int i=0;i planeStrengthCombo[plane][i]) planeStrengthCombo[plane][i]=planeStrength[plane][sIndex][i]; } break; } } double a=1.0/planeStrength[plane].length; switch (matchStatMode) { case 0: // multiply for (int i=0;i0.0) planeStrengthCombo[plane][i]=Math.pow(planeStrengthCombo[plane][i],a); } break; case 1: // add for (int i=0;i0) && // should be positive ((planeIndex[i]<0) ||((planeStrengthCombo[planeIndex[i]]!=null) && (planeStrengthCombo[plane]!=null) && (planeStrengthCombo[planeIndex[i]][iPix] < planeStrengthCombo[plane][iPix])))) { //java.lang.NullPointerException planeIndex[i]=plane; numUsed_dbg++; } } if (debugLevel>3) { System.out.println ("planeLikelyTile("+tileX+","+tileY+", ...) plane="+plane+ " disparity="+zTile.getPlaneDisparity(plane)+" strength="+zTile.getPlaneStrength(plane)+ " used "+numUsed_dbg+"pixels"); // only scan inner pixels? } } if (debugLevel>3) { int numActivePlanes=0; for (int plane=0;plane0){ int index=0; int [] planeNumbers=new int [numActivePlanes]; for (int plane=0;plane=0)?zTile.getPlaneDisparity( planeIndex[i]):0.0); // make it NaN? zTile.setAux(0,aux0); } /** * calculate auto/inter correaltion of the smaller subtiles * @param doubleFHT - DHT instance reused by the thread (should be initialized) * @param data first/second array - now [2][32*32] * @param window - subtile window (defines subtile size) - now 8x8=64 - has to be initialized to an array with [0] equal to NaN * @param phaseCoeff phase correlation fraction (0 - normal, 1.0 - pure phase) * @param highPassSigma high-pass sigma - fraction of the full range * @param lowPassSigma low-pass sigma, in pixels * @param debugLevel * @return for each row and column - value of the correlation in the center (auto 00, auto 11, inter 01): [vert][hor]{corr00, corr11, corr01} */ public double [][][] subTileCorrelation( DoubleFHT doubleFHT, // will generate if null, can be used to reuse initialization double [][] data, // difines size double [] window, // defines tile size double phaseCoeff, // phase correlation fraction (0 - normal, 1.0 - pure phase) double highPassSigma, double lowPassSigma, int debugLevel){ int size=(int) Math.sqrt(data[0].length); // 32 int tileSize=(int) Math.sqrt(window.length); // 8 int tileStep=tileSize/2; //4 int tileExtra=size-tileSize; // 24?? int tileLength=tileSize*tileSize; // 64 // int tileCenter=(tileSize+1)*tileSize/2; // 36 int numTilesRow=size/(2*tileStep); // 4 double [][][] corrs=new double[numTilesRow][numTilesRow][3]; if (Double.isNaN(window[0])){ double [] window1d=doubleFHT.getHamming1d(tileSize); // Hamming for (int i=0;iiXYtl[0]/this.overlapStep, iXYbr[1]/this.overlapStep>iXYtl[1]/this.overlapStep}; int tileX=iXYtl[0]/this.overlapStep; int tileY=iXYtl[1]/this.overlapStep; boolean [] bothXY={ ((int) Math.floor(((double) iXYbr[0])/this.overlapStep))>((int) Math.floor(((double)iXYtl[0])/this.overlapStep)), ((int) Math.floor(((double) iXYbr[1])/this.overlapStep))>((int) Math.floor(((double)iXYtl[1])/this.overlapStep))}; */ boolean [] bothXY={ Math.floor(((double) iXYbr[0])/this.overlapStep)>Math.floor(((double)iXYtl[0])/this.overlapStep), Math.floor(((double) iXYbr[1])/this.overlapStep)>Math.floor(((double)iXYtl[1])/this.overlapStep)}; int tileX=(int)Math.floor(((double) iXYtl[0])/this.overlapStep); int tileY=(int)Math.floor(((double) iXYtl[1])/this.overlapStep); if (debugLevel>3) { //(tileX<0) || (tileY<0)){ System.out.println("getNonOccluded(thisZMap,"+xc+","+yc+","+disparity+ ","+foregroundDisparityMargin+","+disparityTolerance+","+debugLevel+ ") :iXYtl={"+iXYtl[0]+","+iXYtl[1]+"} :iXYbr={"+iXYbr[0]+","+iXYbr[1]+"} :dXY={"+dXY[0]+","+dXY[1]+"} tileX="+ tileX+" tileY="+tileY); } double disparityThresholdFg=disparity+foregroundDisparityMargin; // enMasks[0][0]=thisZMap[tileY][tileX].getNonOccluded(disparityThresholdFg); // enMasks[0][1]=(bothXY[0])?thisZMap[tileY][tileX+1].getNonOccluded(disparityThresholdFg):null; // enMasks[1][0]=(bothXY[1])?thisZMap[tileY+1][tileX].getNonOccluded(disparityThresholdFg):null; // enMasks[1][0]=(bothXY[0]&&bothXY[1])?thisZMap[tileY+1][tileX+1].getNonOccluded(disparityThresholdFg):null; enMasks[0][0]=getEnabledNonOccluded( thisZMap, tileX, tileY, disparityThresholdFg, disparity, disparityTolerance, debugLevel); enMasks[0][1]=(bothXY[0])? getEnabledNonOccluded( thisZMap, tileX+1, tileY, disparityThresholdFg, disparity, disparityTolerance, debugLevel):null; enMasks[1][0]=(bothXY[1])? getEnabledNonOccluded( thisZMap, tileX, tileY+1, disparityThresholdFg, disparity, disparityTolerance, debugLevel):null; enMasks[1][1]=(bothXY[0]&&bothXY[1])?getEnabledNonOccluded( thisZMap, tileX+1, tileY+1, disparityThresholdFg, disparity, disparityTolerance, debugLevel):null; // int paddedSize=thisZMap[tileY][tileX].getPaddedSize(); // int margin=this.overlapStep/2-thisZMap[tileY][tileX].getOverlap(); // int padding=thisZMap[tileY][tileX].getOverlap(); int padding=getPadding(); int [][] whereX=new int [this.paddedSize+(bothXY[0]?1:0)][2]; int [][] whereY=new int [this.paddedSize+(bothXY[1]?1:0)][2]; int [] tile00tl= {tileX*this.overlapStep-padding,tileY*this.overlapStep-padding}; for (int i=0;i=(tile00tl[0]+this.overlapStep))){ whereX[i][0]-=this.overlapStep; whereX[i][1]=1; } } for (int i=0;i=(tile00tl[1]+this.overlapStep))){ whereY[i][0]-=this.overlapStep; whereY[i][1]=1; } } int longPaddedSize=this.paddedSize+1; boolean [] extraBits=new boolean[longPaddedSize*longPaddedSize]; for (int y=0;y=enMasks[whereY[y][1]][whereX[x][1]].length){ System.out.println( " xc="+xc+ " yc="+yc+ " tileX="+tileX+ " tileY="+tileY+ " iXYtl={"+iXYtl[0]+","+iXYtl[1]+"}"+ " iXYbr={"+iXYbr[0]+","+iXYbr[1]+"}"+ " bothXY={"+bothXY[0]+","+bothXY[1]+"}"+ " whereX.length="+whereX.length+ " whereY.length="+whereY.length+ " whereY["+y+"][0]="+whereY[y][0]+ " whereY["+y+"][1]="+whereY[y][1]+ " whereX["+x+"][0]="+whereX[x][0]+ " whereX["+x+"][1]="+whereX[x][1]+ " enMasks["+whereY[y][1]+"]["+whereX[x][1]+"].length="+enMasks[whereY[y][1]][whereX[x][1]].length+ " whereY[y][0]*this.paddedSize + whereX[x][0]="+(whereY[y][0]*this.paddedSize + whereX[x][0])+ " Math.floor(((double)iXYtl[0])/this.overlapStep)="+Math.floor(((double)iXYtl[0])/this.overlapStep)+ " Math.floor(((double)iXYbr[0])/this.overlapStep)="+Math.floor(((double)iXYbr[0])/this.overlapStep)+ " Math.floor(((double)iXYtl[1])/this.overlapStep)="+Math.floor(((double)iXYtl[1])/this.overlapStep)+ " Math.floor(((double)iXYbr[1])/this.overlapStep)="+Math.floor(((double)iXYbr[1])/this.overlapStep)+ " (int) Math.floor(((double)iXYtl[0])/this.overlapStep)="+((int) Math.floor(((double)iXYtl[0])/this.overlapStep))+ " (int) Math.floor(((double)iXYbr[0])/this.overlapStep)="+((int) Math.floor(((double)iXYbr[0])/this.overlapStep))+ " (int) Math.floor(((double)iXYtl[1])/this.overlapStep)="+((int) Math.floor(((double)iXYtl[1])/this.overlapStep))+ " (int) Math.floor(((double)iXYbr[1])/this.overlapStep)="+((int) Math.floor(((double)iXYbr[1])/this.overlapStep))/*+ " ((int) (Math.floor((double)iXYbr[1])/this.overlapStep))>((int) (Math.floor((double)iXYtl[1])/this.overlapStep))="+ (((int) (Math.floor((double)iXYbr[1])/this.overlapStep))>((int) (Math.floor((double)iXYtl[1])/this.overlapStep)))+ " ((Math.floor((double)iXYbr[1])/this.overlapStep))>((Math.floor((double)iXYtl[1])/this.overlapStep))="+ (((Math.floor((double)iXYbr[1])/this.overlapStep))>((Math.floor((double)iXYtl[1])/this.overlapStep)))+ " "+((int) (Math.floor((double)iXYbr[1])/this.overlapStep))+">"+((int) (Math.floor((double)iXYtl[1])/this.overlapStep))+"="+ (((int) (Math.floor((double)iXYbr[1])/this.overlapStep))>((int) (Math.floor((double)iXYtl[1])/this.overlapStep)))*/ ); } /* ((int) (Math.floor((double)iXYbr[0])/this.overlapStep))>((int) (Math.floor((double)iXYtl[0])/this.overlapStep)), ((int) (Math.floor((double)iXYbr[1])/this.overlapStep))>((int) (Math.floor((double)iXYtl[1])/this.overlapStep))}; */ extraBits[index++]=enMasks[whereY[y][1]][whereX[x][1]][whereY[y][0]*this.paddedSize + whereX[x][0]]; } } if (debugLevel>5){ (new ShowDoubleFloatArrays()).showArrays( extraBits, longPaddedSize, longPaddedSize, "EB_-X"+tileX+"-Y"+tileY ); } int index=0; double [] result=new double[this.paddedSize*this.paddedSize]; double [][]k={ {(1-dXY[0])*(1-dXY[1]), ( dXY[0])*(1-dXY[1])}, {(1-dXY[0])*( dXY[1]), ( dXY[0])*( dXY[1])}}; for (int y=0;y=this.tilesX) || (tileY>=this.tilesY) || (thisZMap[tileY][tileX]==null)){ boolean [] empty=new boolean[this.paddedSize*this.paddedSize]; for (int i=0;i5){ boolean [] geno=thisZMap[tileY][tileX].getEnabledNonOccluded( disparityThresholdFg, disparity, disparityTolerance, debugLevel); int dbgSize=(int)Math.sqrt(geno.length); (new ShowDoubleFloatArrays()).showArrays( geno, dbgSize, dbgSize, "GENO_-X"+tileX+"-Y"+tileY ); } return thisZMap[tileY][tileX].getEnabledNonOccluded( disparityThresholdFg, disparity, disparityTolerance, debugLevel); } /** * Calculate fine-step (4 pix?) correlation between the pair of square images (32x32) that are already shifted to compensate for known disparity * only center area (with 1/4 margins) will be calculated, if the correlation is strong enough, the updated disparity arrays will be calculated fro these tiles * * @param data a pair of square arrays to be correlated (32x32 pixels) * @param window cosine mask (8x8=64 pixels long). If the center element is 0.0 - will generate array * @param dXY unity vector in the direction of the disparity between the two images * @param phaseCoeff 0.0 - normal correlation, 1.0 - pure phase correlation * @param debugLevel debug level * @return smooth array, same dimension as data[0], with only center area trusted, proportional to correlation at zero */ public double [] localCorrelation( DoubleFHT doubleFHT, // will generate if null, can be used to reuse initialization double [][] data, // difines size double [] window, // defines tile size double [] dXY, // unity vector defines disparity direction double phaseCoeff, // phase correlation fraction (0 - normal, 1.0 - pure phase) double highPassSigma, double lowPassSigma, int normalize, int debugLevel ){ return localCorrelation( doubleFHT, // will generate if null, can be used to reuse initialization data, // difines size window, // defines tile size dXY, // unity vector defines disparity direction phaseCoeff, // phase correlation fraction (0 - normal, 1.0 - pure phase) highPassSigma, lowPassSigma, normalize, debugLevel, null); } public double [] localCorrelation( DoubleFHT doubleFHT, // will generate if null, can be used to reuse initialization double [][] data, // difines size double [] window, // defines tile size double [] dXY, // unity vector defines disparity direction double phaseCoeff, // phase correlation fraction (0 - normal, 1.0 - pure phase) double highPassSigma, double lowPassSigma, int normalize, int debugLevel, String title ){ int size=(int) Math.sqrt(data[0].length); // 32 int tileSize=(int) Math.sqrt(window.length); // 8 int tileStep=tileSize/2; //4 int tileExtra=size-tileSize; // 24?? int tileLength=tileSize*tileSize; // 64 int tileCenter=(tileSize+1)*tileSize/2; // 36 int numTilesRow=size/(2*tileStep); // 4 if (doubleFHT==null) doubleFHT= new DoubleFHT(); if (Double.isNaN(window[0])){ double [] window1d=doubleFHT.getHamming1d(tileSize); // Hamming for (int i=0;i3) && (tileY==0) && (tileX==0)) for (int i=0;i4){ double d=Math.sqrt(dxA*dxA+dyA*dyA); System.out.println("getShiftedSlices(): xc1="+xc1+" yc1="+yc1+" size="+size+" first="+first+" second="+second); System.out.println("getShiftedSlices(): d="+IJ.d2s(d,2)+" dX="+IJ.d2s(dxA,2)+" dY="+IJ.d2s(dyA,2)); System.out.println("getShiftedSlices(): shiftImage[0]="+shiftImage[0]+" shiftImage[1]="+shiftImage[1]); System.out.println("getShiftedSlices() first image: dX="+IJ.d2s(dx[0],2)+" dY="+IJ.d2s(dy[0],2)); System.out.println("getShiftedSlices() second image: dX="+IJ.d2s(dx[1],2)+" dY="+IJ.d2s(dy[1],2)); System.out.println("getShiftedSlices() second image: iDx="+iDx+" iDyY="+iDy); System.out.println("getShiftedSlices() second image: xc="+(xc1+iDx)+" yc="+(yc1+iDy)); } int doubleSize=2*size; int doubleLength=doubleSize*doubleSize; double [] slice= new double[doubleLength]; double [] imgSlice; int [] img= {first,second}; /* if (window==null) { window = new double [doubleLength]; window[0]=Double.NaN; } */ if (Double.isNaN(window[0])) { double[] window1d=doubleFHT.getHamming1d(size); // Hamming int index=0; int halfSize=size/2; int size32=3*size/2; for (int iy=0;iysize32)?window1d[iy-size]:1.0); for (int ix=0;ixsize32)?window1d[ix-size]:1.0); window[index++]=wx*wy; } } } if (debugLevel>4){ System.out.print("getShiftedSlices(): channelMask="+channelMask); if (debugLevel>3){ (new ShowDoubleFloatArrays()).showArrays( window, doubleSize, doubleSize, "window"); } } for (int iImg=0;iImg2) System.out.println("xc["+iImg+"]="+xc[iImg]+" yc["+iImg+"]="+yc[iImg]+" shiftImage["+iImg+"]="+shiftImage[iImg]); slice= getSelection( imgSlice, // source image/channel slice slice, doubleSize, //int width, doubleSize, //int height, imageWidth, xc[iImg] , //xc, yc[iImg]); //yc); if (debugLevel>4) { (new ShowDoubleFloatArrays()).showArrays( slice, doubleSize, doubleSize, "slice-getShiftedSlices"); } // normalize and save DC double dc=0.0; if (shiftImage[iImg]){ dc=normalizeAndWindowGetDC (slice, window); //windowInterpolation if (debugLevel>4) { (new ShowDoubleFloatArrays()).showArrays( slice, doubleSize, doubleSize, "slice-normalized-dc"+dc); } // doubleFHT.shift(slice, dx[iImg], dy[iImg]); doubleFHT.shift(slice, -dx[iImg], -dy[iImg]); if (debugLevel>4) { (new ShowDoubleFloatArrays()).showArrays( slice, doubleSize, doubleSize, "slice-shifted"); } } else { if (debugLevel>2) System.out.println("No shift is needed"); } // int oSliceNumber=img.length*cN+iImg; if (debugLevel>2) System.out.println("getShiftedSlices() dc="+dc); if (doubleSizeOutput) { if (debugLevel>2) System.out.println("doubleLength="+doubleLength+"doubleSize="+doubleSize); result[channel][iImg]=new double [doubleLength]; int oIndex=0; for (int iY=0;iY=result[channel][iImg].length) || (oIndex>=slice.length) || (oIndex>=window.length)){ System.out.println("iX="+iX+" iY="+iY+" oIndex="+oIndex); } result[channel][iImg][oIndex]=shiftImage[iImg]?(slice[oIndex]/window[oIndex]+dc):slice[oIndex];// no NaN with Hamming oIndex++; } } }else{ result[channel][iImg]=new double [size*size]; int iIndex=size*size+size/2; // top left index of the inner square size*size int oIndex=0; for (int iY=0;iY=fullHeight); for (int ix=0;ix=fullWidth)) { if (oIndex>=selection.length) System.out.println("\ngetSelection(imageSlice["+imageSlice.length+"],selection["+selection.length+"]"+ ","+fullWidth+","+width+","+height+","+xc+","+yc+") ix="+ix+" iy="+iy+" oIndex="+oIndex); selection[oIndex]=0.0; } else { selection[oIndex]=imageSlice[srcY*fullWidth+srcX]; } } } return selection; } public void setupZMap( Rectangle woi, // in tiles - may be final int nImg, final int maxNumber, final double minFirst, final double minAbsolute, final double minRelative, final double mergeMax, final int overlap, final double zMapMinForeground, final int threadsMax, final boolean showProgress, final int debugLevel){ if (this.centerPixels==null) { String msg="Centered disparity data is not defined"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if (this.syntheticPixels==null) moveDisparityFromCenter(threadsMax,showProgress,debugLevel); final float [] thisSyntheticPixels= this.syntheticPixels[nImg]; if (woi==null) woi=new Rectangle (0, 0, this.tilesX,this.tilesY); this.zMapWOI=new Rectangle(); this.zMapWOI.x=(woi.x>=0)?woi.x:0; this.zMapWOI.y=(woi.y>=0)?woi.y:0; this.zMapWOI.width= (((woi.x+woi.width) <=this.tilesX)?(woi.x+woi.width): this.tilesX)-this.zMapWOI.x; this.zMapWOI.height=(((woi.y+woi.height)<=this.tilesY)?(woi.y+woi.height):this.tilesY)-this.zMapWOI.y; final Rectangle zMapWOI=this.zMapWOI; if ((this.zMap==null) || (this.zMap[nImg]==null)) initZMap(nImg); final ZTile [][] thisZMap=this.zMap[nImg]; final int tilesX=this.zMapWOI.width; final int tiles=this.zMapWOI.width*this.zMapWOI.height; if (debugLevel>2) System.out.println("setupZMap() woi.x="+woi.x+" woi.y="+woi.y+ " woi.width="+woi.width+" woi.height="+woi.height); if (debugLevel>2) System.out.println("setupZMap() zMapWOI.x="+zMapWOI.x+" zMapWOI.y="+zMapWOI.y+ " zMapWOI.width="+zMapWOI.width+" zMapWOI.height="+zMapWOI.height); this.paddedSize=this.overlapStep+2*overlap; this.innerMask=new BitSet(this.paddedSize*this.paddedSize); for (int i=0;ithis.tilesY) tileYmax=this.tilesY; int tileXmin=woi.x/this.overlapStep; int tileXmax=(xRight-1)/this.overlapStep+1; // +1 to the last needed if (tileXmax>this.tilesX) tileXmax=this.tilesX; for (int i=0;i=0) && (oY=0) && (oXthis.tilesY) tileYmax=this.tilesY; int tileXmin=woi.x/this.overlapStep; int tileXmax=(xRight-1)/this.overlapStep+1; // +1 to the last needed if (tileXmax>this.tilesX) tileXmax=this.tilesX; for (int i=0;i=0) && (oY=0) && (oX2) System.out.println("setupZMapTile("+tileX+","+tileY+",...)"); boolean debugThisTile=debugLevel>3; thisZMap[tileY][tileX]=new ZTile(); ZTile zTile=thisZMap[tileY][tileX]; double [] correlationSection = getTileDisparity(tileX, tileY, thisDisparityTiles); // double threshold=minAbsolute; boolean [] isMax =new boolean[correlationSection.length]; int iMax=-1; int numMax=0; for (int i=0;i=minAbsolute) && ((i==0)||(correlationSection[i]>=correlationSection[i-1])) && ((i==(correlationSection.length-1)) || (correlationSection[i]>=correlationSection[i+1]))){ isMax[i]=true; // local max above absolute threshold if ((numMax==0) || (correlationSection[i]>correlationSection[iMax])) iMax=i; numMax=1; } else { isMax[i]=false; } } // double mergeMax=1.5; //merge maximums for interpolation if closer than mergeMax; int iMergeMax=(int) Math.round(mergeMax/this.disparityPerEntry); if (debugThisTile){ System.out.println("mergeMax="+mergeMax+" iMergeMax="+iMergeMax); } if ((numMax>0) && (correlationSection[iMax]=threshold) numMax++; numMax++; //else isMax[i]=false; } } if (numMax>maxNumber) numMax=maxNumber; // limit to specified number of correlation maximums to subpixel int [] maxIndices=new int [numMax]; maxIndices[0]=iMax; isMax[iMax]=false; int maxNum; for (maxNum=1;maxNum=isMax.length) break; if (isMax[maxIndices[maxNum-1]+i]){ nearUp = maxIndices[maxNum-1]+i; break; } } int n=1+((nearDown>=0)?1:0)+((nearUp>=0)?1:0); if (n>1){ double s=maxIndices[maxNum-1]+((nearDown>=0)?nearDown:0)+((nearUp>=0)?nearUp:0); int iMerged=(int) Math.round(s/n); if (debugThisTile){ System.out.println("Merging close maximums: "+maxIndices[maxNum-1]+" with "+ ((nearDown>=0)?nearDown:"")+" "+((nearUp>=0)?nearUp:"")+" to "+iMerged); } maxIndices[maxNum-1]=iMerged; isMax[iMerged]=false; if (nearDown>=0) isMax[nearDown]=false; if (nearUp>=0) isMax[nearUp]=false; } iMax=-1; for (int i=0;i=correlationSection[iMax]))) iMax=i; } if (iMax<0) break; // no more maximums isMax[iMax]=false; maxIndices[maxNum]=iMax; } if (debugThisTile){ System.out.println("List maximums on correlation section"); for (int n=0;n=this.enabledPixels.length) return; // or make it an error? int paddedSize= this.size+this.overlap*2; this.enabledPixels[this.foregroundIndex]=new BitSet(paddedSize*paddedSize); this.enabledPixels[this.foregroundIndex].set(0,paddedSize*paddedSize); this.enabledPlane[this.foregroundIndex]=true; } public boolean isForegroundValid(){ if (this.foregroundIndex>=this.maximums.length) return false; // no foreground if (!this.enabledPlane[this.foregroundIndex]) return false; // disabled plane if (this.enabledPixels[this.foregroundIndex]== null) return true; // all pixels enabled BitSet bb=(BitSet) this.enabledPixels[this.foregroundIndex].clone(); bb.and(this.innerMask); if (!bb.isEmpty()) return true; return false; } public boolean advanceForeground(){ while (!isForegroundValid()){ if (this.foregroundIndex>=this.maximums.length) return false; // no foreground left this.foregroundIndex++; } return true; } public float [] getZmap(){ if (this.zmap!=null) return this.zmap; int paddedSize=this.size+2*this.overlap; int paddedLength=paddedSize*paddedSize; float [] map= new float [paddedLength]; BitSet needed=null; BitSet newBits=null; for (int plane=this.foregroundIndex; plane=0;i=newBits.nextSetBit(i+1)){ map[i]=(float) this.maximums[plane][0]; } needed.andNot(newBits); if (this.enabledPixels[plane]==null) return map; if (needed.isEmpty()) return map; } if (needed==null){ // no maximums at all, probably for (int i=0;i=0;i=needed.nextSetBit(i+1))map[i]=0.0F; return map; } /* public boolean [] getNonOccluded(double disparity){ int paddedSize=this.size+2*this.overlap; int paddedLength=paddedSize*paddedSize; BitSet nonOccludedBits=new BitSet(paddedLength); nonOccludedBits.set(0,paddedLength); for (int plane=0;(planedisparity);plane++) if (this.enabledPlane[plane]) { if (this.certainPixels[plane]!=null) nonOccludedBits.andNot(this.certainPixels[plane]); } boolean [] nonOccluded=new boolean[paddedLength]; for (int i=0;i3) System.out.println(" ---- getEnabledNonOccluded("+disparityFG+","+ disparity+","+ disparityTolerance+","+ debugLevel+")"); int paddedSize=this.size+2*this.overlap; int paddedLength=paddedSize*paddedSize; BitSet nonOccludedBits=new BitSet(paddedLength); nonOccludedBits.set(0,paddedLength); for (int plane=0;(planedisparityFG);plane++) if (this.enabledPlane[plane]) { if (this.certainPixels[plane]!=null) nonOccludedBits.andNot(this.certainPixels[plane]); if (debugLevel>3) System.out.println(" ------ plane="+plane+" cumul. nonOccludedBits.cardinality()="+nonOccludedBits.cardinality()); } if (!Double.isNaN(disparityTolerance)){ BitSet enabledBits=new BitSet(paddedLength); for (int plane=0;plane3) System.out.println(" ------ plane="+plane+" cumul. enabledBits.cardinality()="+enabledBits.cardinality()); } nonOccludedBits.and(enabledBits); if (debugLevel>3) System.out.println(" ------ result nonOccludedBitscardinality()="+nonOccludedBits.cardinality()); } boolean [] nonOccluded=new boolean[paddedLength]; for (int i=0;iaMax) aMax=this.maximums[i][1]; aMax=Math.max(aMax*this.minRelative,this.minAbsolute); for (int i=0;i=aMax; if (keepFG && (this.foregroundIndex=0)) { this.enabledPlane[this.foregroundIndex]=true; } } public void setForeGround( double minForeground){ if (!Double.isNaN(minForeground)) this.foregroundThreshold=minForeground; for (this.foregroundIndex=0; (this.foregroundIndex=this.subdivAverage) countAvg=this.subdivAverage-1; int countDiff= (int)Math.round(0.5*(v2-v1))+subdivHalfDifference; if (countDiff<0) countDiff=0; if (countDiff>2*this.subdivHalfDifference) countDiff=2*this.subdivHalfDifference; staging[countDiff*this.subdivAverage+countAvg]+=weight; } public void addStagingSample( double []staging, int chn, double weight, double v1, double v2, // reduce weight depending on difference, scale to measured variance double scaleVariance, double kLocal, // 0 - use global varaiance, 1.0 - use local int nImg1, // first image number int nImg2, // second image number double var1, // first image variance double var2, // second image variance boolean debug){ double min=this.valueLimits[chn][0]; double step=(this.valueLimits[chn][1]-this.valueLimits[chn][0])/(this.subdivAverage-1); double av=0.5*(v1+v2); int countAvg=(int) Math.round((av-min)/step); if (countAvg<0) countAvg=0; if (countAvg>=this.subdivAverage) countAvg=this.subdivAverage-1; double diff=0.5*(v2-v1); int countDiff= (int)Math.round(diff/step)+subdivHalfDifference; if (countDiff<0) countDiff=0; if (countDiff>2*this.subdivHalfDifference) countDiff=2*this.subdivHalfDifference; if (debug){ System.out.println("addStagingSample(...,"+chn+","+weight+","+v1+","+v2+","+scaleVariance+","+ kLocal+","+nImg1+","+nImg2+","+var1+","+var2); System.out.println(" +++ min="+min+" step="+step+" av="+av+" diff="+diff+" countAvg="+countAvg+" countDiff="+countDiff); } if (scaleVariance>0.0){ if (kLocal<1.0){ // mix local variance with average over all image int count1=(int) Math.round((v1-min)/step); if (count1<0) count1=0; if (count1>=this.subdivAverage) count1=this.subdivAverage-1; int count2=(int) Math.round((v2-min)/step); if (count2<0) count2=0; if (count2>=this.subdivAverage) count2=this.subdivAverage-1; if (kLocal<0) kLocal=0; var1=kLocal*var1+(1.0-kLocal)*this.standardVariance[nImg1][chn][count1]; var2=kLocal*var2+(1.0-kLocal)*this.standardVariance[nImg2][chn][count2]; } double sigma=scaleVariance*Math.sqrt(var1*var1); weight*=Math.exp(-diff*diff/(2.0*sigma*sigma))/sigma/Math.sqrt(2*Math.PI); if (debug) System.out.println(" +++ sigma="+sigma+" weight="+weight); } staging[countDiff*this.subdivAverage+countAvg]+=weight; if (debug) System.out.println(" staging["+(countDiff*this.subdivAverage+countAvg)+"]="+staging[countDiff*this.subdivAverage+countAvg]); } public double getStrength( double []staging, int chn, double v1, double v2){ double min=this.valueLimits[chn][0]; double step=(this.valueLimits[chn][1]-this.valueLimits[chn][0])/(this.subdivAverage-1); double av=0.5*(v1+v2); int countAvg=(int) Math.floor((av-min)/step); int countAvg1=(int) Math.ceil((av-min)/step); double dx=(av-min)-step*countAvg; if (countAvg<0) { countAvg=0; countAvg1=0; dx=0.0; } if (countAvg1>=this.subdivAverage){ countAvg=this.subdivAverage-1; countAvg1=this.subdivAverage-1; dx=0.0; } double diff=0.5*(v2-v1); int countDiff= (int)Math.floor(diff/step)+this.subdivHalfDifference; int countDiff1= (int)Math.ceil(diff/step)+this.subdivHalfDifference; double dy=diff-step*(countDiff-this.subdivHalfDifference); if (countDiff<0) { countDiff=0; countDiff1=0; dy=0.0; } if (countDiff1>2*this.subdivHalfDifference){ countDiff=2*this.subdivHalfDifference; countDiff1=2*this.subdivHalfDifference; dy=0.0; } if ((countDiff1*this.subdivAverage+countAvg1)>=staging.length){ System.out.println("BUG: getStrength() countAvg="+countAvg+" countAvg1="+countAvg1+ " countDiff="+countDiff+" countDiff1="+countDiff1+" staging.length="+staging.length); } return staging[countDiff *this.subdivAverage+countAvg ]*(1-dy)*(1-dx)+ staging[countDiff *this.subdivAverage+countAvg1]*(1-dy)*( dx)+ staging[countDiff1*this.subdivAverage+countAvg ]*( dy)*(1-dx)+ staging[countDiff1*this.subdivAverage+countAvg1]*( dy)*( dx); } public void blurStaging( double []staging, int chn, double sigma){ if (sigma<=0) return; // double min=this.valueLimits[chn][0]; double step=(this.valueLimits[chn][1]-this.valueLimits[chn][0])/(this.subdivAverage-1); (new DoubleGaussianBlur()).blurDouble( staging, this.subdivAverage, 2*this.subdivHalfDifference+1, sigma/step, sigma/step, 0.01); } public Photometric( double [][][] images, int imageWidth, int margins, double ignoreFraction, int subdivAverage, int subdivHalfDifference, // int subdivVariance, double smoothVarianceSigma, double scaleVariance, int debugLevel ){ if (margins<1) margins=1; this.subdivAverage=subdivAverage; this.subdivHalfDifference=subdivHalfDifference; // this.subdivVariance = subdivVariance; this.smoothVarianceSigma=smoothVarianceSigma; this.scaleVariance=scaleVariance; // sigma along difference (vertical axis) will be scaleVariance*variance for this average value this.numImages=images.length; this.valueLimits=new double[channelNames.length][]; int [] dirs1={1,imageWidth+1,imageWidth,imageWidth-1,-1,-imageWidth-1,-imageWidth,-imageWidth+1,0}; this.standardVariance=new double[this.numImages][this.channelNames.length][]; this.averageVariance=new double[this.numImages][this.channelNames.length]; for (int i=0;i2){ System.out.println("nImg="+nImg+" chn="+chn+ " min0="+IJ.d2s(min,3)+" max0="+IJ.d2s(max,3)); } for (int y=margins;y<(imageHeight-margins);y++) for (int x=margins;x<(imageWidth-margins);x++){ if (image[y*imageWidth+x]2) System.out.println("y="+y+" x="+x+" min="+IJ.d2s(min,3)); } if (image[y*imageWidth+x]>max) { max=image[y*imageWidth+x]; if (debugLevel>2) System.out.println("y="+y+" x="+x+" max="+IJ.d2s(max,3)); } } if (debugLevel>2){ System.out.println("nImg="+nImg+" chn="+chn+ " min="+IJ.d2s(min,3)+" max="+IJ.d2s(max,3)); } } int [] histogram=new int [this.histogramSize]; for (int i=0;i2){ System.out.println( " min="+IJ.d2s(min,3)+" max="+IJ.d2s(max,3)+" step="+IJ.d2s(step,6)); } for (int nImg=0;nImg=this.histogramSize) index=this.histogramSize-1; histogram[index]++; //java.lang.ArrayIndexOutOfBoundsException: 1005 } } int totalNum=0; for (int i=0;i2){ System.out.println( " totalNum="+totalNum+" ignoreNumPix="+ignoreNumPix); } this.valueLimits[chn]=new double[2]; int num=0; for (int i=0;i2) System.out.println("--- "+num+" min+"+i+"*step="+IJ.d2s(min+i*step,3)); if (num>=ignoreNumPix){ this.valueLimits[chn][0]=min+i*step; if (debugLevel>2){ System.out.println("i="+i+" min+i*step="+IJ.d2s(min+i*step,3)); } break; } } num=0; for (int i=histogram.length-1;i>=0;i--){ num+=histogram[i]; if (debugLevel>2) System.out.println("--- "+num+" min+"+i+"*step="+IJ.d2s(min+i*step,3)); if (num>=ignoreNumPix){ this.valueLimits[chn][1]=min+i*step; if (debugLevel>2){ System.out.println("i="+i+" min+i*step="+IJ.d2s(min+i*step,3)); } break; } } if (debugLevel>1){ System.out.println("Channel '"+this.channelNames[chn]+ "' min="+IJ.d2s(this.valueLimits[chn][0],3)+" max="+IJ.d2s(this.valueLimits[chn][1],3)); } // calculatye variance for each emage/channel for (int nImg=0;nImg=this.subdivAverage) count=this.subdivAverage-1; this.standardVariance[nImg][chn][count]+=v2; // squared samples[count]++; this.averageVariance[nImg][chn]+=v2; totalSamples++; } for (int i=0;i0) this.standardVariance[nImg][chn][i]=Math.sqrt(this.standardVariance[nImg][chn][i]/samples[i]); else this.standardVariance[nImg][chn][i]=0.0; // if ((debugLevel>1) && (nImg==0) && (chn==0)){ if ((debugLevel>2) && (nImg==0)){ System.out.println("samples["+i+"]="+samples[i]+ " this.standardVariance["+nImg+"]["+chn+"]["+i+"]="+IJ.d2s(this.standardVariance[nImg][chn][i],3)); } } this.averageVariance[nImg][chn]=Math.sqrt(this.averageVariance[nImg][chn]/totalSamples); for (int i=0;i0.0){ (new DoubleGaussianBlur()).blur1Direction( this.standardVariance[nImg][chn], //double [] pixels, this.subdivAverage, //int width, 1, //int height, this.smoothVarianceSigma, //double sigma, 0.01, //double accuracy, true //boolean xDirection: true - horizontal ); } } } // now cteate matchingQuality arrays for each image pair/channel // this.matchingQuality=new double [this.numImages][this.numImages-1][this.valueLimits.length][this.subdivAverage*this.subdivDifference]; this.matchingQuality=new double [this.numImages][this.numImages-1][][]; //[this.subdivAverage*this.subdivDifference]; int subdivDifference=2*this.subdivHalfDifference+1; int matchingQualityLength=this.subdivAverage*subdivDifference; for (int nImg=0;nImg=nImg)?(sIndex+1):sIndex; if ((images[nImg]!=null) && (images[sImg]!=null)){ this.matchingQuality[nImg][sIndex]=new double [this.valueLimits.length][]; for (int chn=0;chn=nImg)?(sIndex+1):sIndex; for (int chn=0;chn2) debugArray=new double[3][data[0].length*subPixel*subPixel]; for (int tileY=0;tileY3) && (tileY==0) && (tileX==0)) for (int i=0;i2){ System.out.println("calculateDisparityTiles(), tileXMin="+tileXMin+", tileXMax="+tileXMax+", tileYMin="+tileYMin+", tileYMax="+tileYMax); } final double [][][][][] disparityTiles= new double [numTilesY][numTilesX][][][]; // first [tileY][tileX][image number][other image index][disparity index] final double [][][][] fhtCache=new double [numTilesY][][][]; for (int tileY=tileYMin;tileY<=tileYMax;tileY++){ for (int tileX=tileXMin;tileX<=tileXMax;tileX++){ disparityTiles[tileY][tileX]=null; } fhtCache[tileY]=null; } final int numSensors=this.channel.length; int minDispalcementTileXAll=minDispalcementTileX[0]; int maxDispalcementTileXAll=maxDispalcementTileX[0]; int minDispalcementTileYAll=minDispalcementTileY[0]; int maxDispalcementTileYAll=maxDispalcementTileY[0]; for (int i=1;iminDispalcementTileX[i]) minDispalcementTileXAll=minDispalcementTileX[i]; if (maxDispalcementTileXAllminDispalcementTileY[i]) minDispalcementTileYAll=minDispalcementTileY[i]; if (maxDispalcementTileYAll2){ System.out.println("calculateDisparityTiles(), minDispalcementTileXAll="+minDispalcementTileXAll+", maxDispalcementTileXAll="+maxDispalcementTileXAll); System.out.println("calculateDisparityTiles(), minDispalcementTileXAll="+minDispalcementTileXAll+", maxDispalcementTileXAll="+maxDispalcementTileXAll); System.out.println("calculateDisparityTiles(), minDispalcementTileYAll="+minDispalcementTileYAll+", maxDispalcementTileYAll="+maxDispalcementTileYAll); System.out.println("calculateDisparityTiles(), minDispalcementTileYAll="+minDispalcementTileYAll+", maxDispalcementTileYAll="+maxDispalcementTileYAll); for (int i=0;i2){ System.out.println("==== calculateDisparityTiles(), correlating channel "+channelName[finalChnYCbCr]+" tileY="+tileY+" ===="); } // free memory - delete the full row of unneeded FHT tiles (all images) if ((tileY+minDispalcementTileYAll)>0) { if (debugLevel>2){ System.out.println("calculateDisparityTiles(), freeing tile row "+(tileY+minDispalcementTileYAll-1)+" for all images"); } fhtCache[tileY+minDispalcementTileYAll-1]=null; for (int nImg=0;nImg=0){ if (debugLevel>2){ System.out.println("calculateDisparityTiles(), freeing tile row "+tileYDel+" for image "+nImg); } if (fhtCache[tileYDel]!=null) fhtCache[tileYDel][nImg]=null; if (nextCachedY[nImg]<=tileYDel) nextCachedY[nImg]= tileYDel+1; // for filling up cache initially only } } // calculate rows of FHT tiles for (int nImg=0;nImg=numTilesX)correctedTileXMax=numTilesX-1; for (;nextCachedY[nImg]<=tileY+maxDispalcementTileY[nImg];nextCachedY[nImg]++) if (nextCachedY[nImg]=0.0) || (correlationLowPassSigma>=0.0); if (useFilter) doubleFHT.createFrequencyFilter( window, // just to specify size correlationHighPassSigma, correlationLowPassSigma); double [][][] stage=null; // [pY][pX]{data, weight} for (int rsltTile=rsltTileAtomic.getAndIncrement(); rsltTile2) && (finalChnYCbCr==0) && (tileY==tileYMin) &&(rsltTile==0); // make boolean debugThisTile=(debugLevel>2) && (finalChnYCbCr==0) && (tileY==tileYMin) &&(rsltTile==1); // make int nImg=firstSecondIndex[rsltTile%firstSecondIndex.length][0]; int secondIndex=firstSecondIndex[rsltTile%firstSecondIndex.length][1]; int secondImage=otherImage[nImg][secondIndex]; int [][] thisDisplacementTilesList=displacementTilesList[nImg][secondIndex]; if ((stage==null) || (stage.length!= correlationMap[nImg][secondIndex].length) || (stage[0].length!=correlationMap[nImg][secondIndex][0].length) ){ stage=new double [correlationMap[nImg][secondIndex].length][correlationMap[nImg][secondIndex][0].length][2]; } double [] zero2={0.0,0.0}; // TODO - zero only needed pixels? for (int pY=0;pY=0) && (tileY=0) && (tileX=0) && (tY=0) && (tX=0) && (stageY=0) && (stageX4)) { System.out.println(tX+" "+tY+" "+index+" "+stageY+" "+stageX+" "+result[index]+" "+window[index]); } } } } else { if (debugThisTile) System.out.println("!!!!! No FHT data for tile tY="+tY+" tX="+tX); // possible debug here - matching tile FHT does not exist } } if (debugThisTile && (debugLevel>4)){ for (int dbg_i=0;dbg_i0)?(stage[dbgY][dbgX][0]/stage[dbgY][dbgX][1]):0.0; debugStage[1][i]=stage[dbgY][dbgX][0]; debugStage[2][i]=stage[dbgY][dbgX][1]; } String [] titles={"result","data","weight"}; (new ShowDoubleFloatArrays()).showArrays( debugStage, stage[0].length, stage.length, true, "C"+nImg+"-"+secondImage+"_tY"+tileY+"_tX"+tileX+"-"+channelName[finalChnYCbCr], titles); } // result accumulated data will need to be divided by weights. Maybe later do only on some? double []correlationSection=new double [disparitySteps+1]; for (int pY=0;pY=threshold) && ((i==0)||(correlationSection[i]>=correlationSection[i-1])) && ((i==(correlationSection.length-1)) || (correlationSection[i]>=correlationSection[i+1]))){ isMax[i]=true; // local max above absolute threshold if ((numMax==0) || (correlationSection[i]>correlationSection[iMax])) iMax=i; numMax=1; } else { isMax[i]=false; } } double mergeMax=1.5; //merge maximums for interpolation if closer than mergeMax; int iMergeMax=(int) Math.round(mergeMax*disparitySteps/maxD); if (debugThisTile){ System.out.println("mergeMax="+mergeMax+" iMergeMax="+iMergeMax); } if (numMax>0){ numMax=0; threshold=correlationSection[iMax]*subpixRMax; for (int i=0;i=threshold) numMax++; else isMax[i]=false; } if (numMax>subpixNMax) numMax=subpixNMax; // limit to specified number of correlation maximums to subpixel int [] maxIndices=new int [numMax]; maxIndices[0]=iMax; isMax[iMax]=false; int maxNum; for (maxNum=1;maxNumdisparitySteps) break; if (isMax[maxIndices[maxNum-1]+i]){ nearUp = maxIndices[maxNum-1]+i; break; } } int n=1+((nearDown>=0)?1:0)+((nearUp>=0)?1:0); if (n>1){ double s=maxIndices[maxNum-1]+((nearDown>=0)?nearDown:0)+((nearUp>=0)?nearUp:0); int iMerged=(int) Math.round(s/n); if (debugThisTile){ System.out.println("Merging close maximums: "+maxIndices[maxNum-1]+" with "+ ((nearDown>=0)?nearDown:"")+" "+((nearUp>=0)?nearUp:"")+" to "+iMerged); } maxIndices[maxNum-1]=iMerged; isMax[iMerged]=false; if (nearDown>=0) isMax[nearDown]=false; if (nearUp>=0) isMax[nearUp]=false; } iMax=-1; for (int i=0;i=correlationSection[iMax]))) iMax=i; } if (iMax<0) break; // no more maximums isMax[iMax]=false; maxIndices[maxNum]=iMax; } //TODO: disable max closer than 1.5pix - they will be covered by previous interpolation anyway if (debugThisTile){ System.out.println("List maximums on correlation section"); double d=Math.sqrt(maxDX*maxDX+maxDY*maxDY)/disparitySteps; for (int n=0;n3)){ (new ShowDoubleFloatArrays()).showArrays( interpolationInput, interpolationSize, interpolationSize, "IW"+n+"-"+iXTL+"-"+iYTL); } if (interpolationFHT==null) interpolationFHT=new DoubleFHT(); double [] upsampled=interpolationFHT.upsample(interpolationInput,interpolationUpSample); // add scalind to upsample() if (debugThisTile && (debugLevel>3)){ (new ShowDoubleFloatArrays()).showArrays( upsampled, interpolationSize*interpolationUpSample, interpolationSize*interpolationUpSample, "RO"+n+"-"+iXTL+"-"+iYTL); } // divide by scaled window, add removed earlier DC level for (int i=0;i0) // now we have correlationSection[], need to combine multiple colors for (int di=0;di2){ System.out.println("getFHTRow(), tileY="+tileY); } int numSensors=this.channel.length; // if (fhtCache[tileY]==null) fhtCache[tileY]=new double [numSensors][numTilesX][]; if (fhtCache[tileY]==null) { fhtCache[tileY]=new double [numSensors][][]; for (int i=0;i2){ System.out.println("getFHTRow(), selecting image "+numImg+" tileX="+tileX+" xc="+(overlapStep*tileX)+" yc="+(overlapStep*tileY)); } getSelection( imageSliceAlpha, // one image/channel slice alphaSlice, corrFFTSize, //int width, corrFFTSize, //int height, overlapStep*tileX , //xc, overlapStep*tileY); //yc); for (int i=0;i0){ System.out.println("Selection ("+(useBinaryAlpha?"binary":"analog")+" alpha ) "+ " iCenterXY[0][0]="+iCenterXY[0][0]+" iCenterXY[0][1]="+iCenterXY[0][1]+" size="+corrFFTSize+ " : first "+IJ.d2s(100*stats[0],3)+"%, second "+IJ.d2s(100*stats[1],3)+"%, overlap "+IJ.d2s(100*stats[2],3)+"%"); } */ if (debugLevel>1){ String [] titles0={"Alpha","Y","Cb","Cr"}; String [] titles=new String[this.overlapImages.length]; for (int i=0;i0){ for (int nPair=0;nPair2) && (l==1)){ double [][] firstSecond={first,second}; String [] firstSecondTitles={"first","second"}; (new ShowDoubleFloatArrays()).showArrays( firstSecond, corrFFTSize, corrFFTSize, true, // "windowed-Y"+iCenterXY[0][0]+"-y"+iCenterXY[0][1]+"-PC"+phaseCorrelationFraction); "windowed-Y"+iFirst+"-:"+iSecond, firstSecondTitles); } // TODO: use common (per-thread) DoubleFHT instance to use caching of sin,cos, filter tables corr[layerResult]=(new DoubleFHT()).correlate ( first, second, correlationHighPassSigma, correlationLowPassSigma, phaseCorrelationFraction); double sigma=(l==1)?noiseNormalizationSignaY:noiseNormalizationSignaCbCr; if (sigma>0){ double rms=hFNoiseNormalize( corr[nPair*numLayers+l], // double [] data, sigma, // double filterSigma, true); // boolean centerOnly); if (this.debugLevel>1){ System.out.println("Correlation RMS for component "+((l==1)?"Y":((l==2)?"Cb":"Cr"))+ " was "+rms); } } for (int j=0;j0){ if (corrRes==null){ System.out.println("Correlation results are below threshold"); } else { System.out.println("Contrast threshold="+contrastThreshold); System.out.println("Correlation lower disparity="+corrRes[0]+", higher disparity="+corrRes[1]+ " Best disparity="+IJ.d2s(corrRes[2],2)+" (contrast="+IJ.d2s(corrRes[3],3)+")"); if (corrRes.length>4){ for (int i=2;i<(corrRes.length/2-1);i++){ System.out.println("Correlation disparity max # "+i+": "+IJ.d2s(corrRes[2*i],2)+ " (contrast="+IJ.d2s(corrRes[2*i+1],3)+")"); } } } } */ return corr; } public double [][] correlate( // old for image pairs boolean autoCorrelation, int corrFFTSize, int corrXC, int corrYC, int corrShift, double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double contrastThreshold, boolean useBinaryAlpha, boolean enableNegativeDisparity, int threadsMax, int debugLevel){ int length=corrFFTSize*corrFFTSize; // double [][] selection = new double[this.overlapImages.length][length]; int numLayers=this.overlapImages.length/2; double [][] selection = getSelection( autoCorrelation?1:0, corrFFTSize, corrXC, corrYC, corrShift); // positive - shift second image, negative - shift first image double [] stats= selectionStats(selection,useBinaryAlpha); if (debugLevel>0){ System.out.println("Selection ("+(useBinaryAlpha?"binary":"analog")+" alpha ) xc="+corrXC+" yc="+corrYC+" size="+corrFFTSize+" shift="+corrShift+ " : first "+IJ.d2s(100*stats[0],3)+"%, second "+IJ.d2s(100*stats[1],3)+"%, overlap "+IJ.d2s(100*stats[2],3)+"%"); } if (debugLevel>1){ String [] titles={"Alpha1","Y1","Cb1","Cr1","Alpha2","Y2","Cb2","Cr2"}; (new ShowDoubleFloatArrays()).showArrays( selection, corrFFTSize, corrFFTSize, true, "selection-x"+corrXC+"-y"+corrYC+"-SHFT"+corrShift+"-PC"+phaseCorrelationFraction, titles); } // correlation stuff double[] hamming1d=(new DoubleFHT()).getHamming1d(corrFFTSize); double [][] window={selection[0].clone(),selection[numLayers].clone()}; for (int iy=0;iy2) && (i==0)){ (new ShowDoubleFloatArrays()).showArrays( first, corrFFTSize, corrFFTSize, "windowed-Y"+corrXC+"-y"+corrYC+"-SHFT"+corrShift+"-PC"+phaseCorrelationFraction); } normalizeAndWindow(second,window[1],true); // TODO: use common (per-thread) DoubleFHT instance to use caching of sin,cos, filter tables corr[i+1]=(new DoubleFHT()).correlate ( first, second, correlationHighPassSigma, correlationLowPassSigma, phaseCorrelationFraction); double sigma=(i==0)?noiseNormalizationSignaY:noiseNormalizationSignaCbCr; if (sigma>0){ double rms=hFNoiseNormalize( corr[i+1], // double [] data, sigma, // double filterSigma, true); // boolean centerOnly); if (this.debugLevel>1){ System.out.println("Correlation RMS for component "+((i==0)?"Y":((i==1)?"Cb":"Cr"))+ " was "+rms); } } // for (int j=0;j0){ if (corrRes==null){ System.out.println("Correlation results are below threshold"); } else { System.out.println("Contrast threshold="+contrastThreshold); System.out.println("Correlation lower disparity="+corrRes[0]+", higher disparity="+corrRes[1]+ " Best disparity="+IJ.d2s(corrRes[2],2)+" (contrast="+IJ.d2s(corrRes[3],3)+")"); if (corrRes.length>4){ for (int i=2;i<(corrRes.length/2-1);i++){ System.out.println("Correlation disparity max # "+i+": "+IJ.d2s(corrRes[2*i],2)+ " (contrast="+IJ.d2s(corrRes[2*i+1],3)+")"); } } } } return corr; } /** * Find min/max positions of above threshold values, positions and values of largest maximums above threshold. * All positions are relative to the center. * @param corr square correlation array (only horizontal middle line is used) * @param threshold minimal correlation value to consider * @param shift add this shift to positions * @param orderMaximums if true - order maximums in descending order, if false - leave them in increasing position (from back to foreground) * @param debugLevel debug level * @return variable length array or null, only defined values are output:null, * {minPosition,maxPosition,firstMaxPosition,firstMaxValue}, * {minPosition,maxPosition,firstMaxPosition,firstMaxValue,secondMaxPosition,secondMaxValue}, ... */ double [] correlationResults( double [] corr, double threshold, double shift, boolean enableNegativeDisparity, boolean orderMaximums, int debugLevel ){ int size = (int) Math.round(Math.sqrt(corr.length)); int index=size*size/2; // start of the middle row; int min=size, max=0,numMax=0; boolean [] localMax=new boolean [size]; int scanMin=enableNegativeDisparity?0:(size/2-((int) shift)); if (scanMin<0) scanMin=0; for (int i=0;ithreshold){ if (i>max) max=i; if (i0) && ((d>=corr[index+i-1]) || ((i==scanMin) && !enableNegativeDisparity)) && (i<(size-1)) && (d>corr[index+i+1])) { localMax[i]=true; numMax++; } if (debugLevel>2) System.out.println("correlationResults(): d="+d+" threshold="+threshold+" i="+i+" (i+shift-size/2="+(i+shift-size/2)+") "+ " min="+min+" max="+max+ " numMax="+numMax); } } if (numMax<1) return null; double [] results= new double [2*(numMax+1)]; results[0]=min-(size/2)+shift; results[1]=max-(size/2)+shift; // first find all maximums, then reorder (relative maximum values may change int iMax=0; for (int i=scanMin;ithreshold){ if (i>max) max=i; if (icorr[index+i])) { if (debugLevel>2) System.out.println("correlationResults(): limiting negative disparity to 0"); results[2*(iMax+1)]= i-size/2+shift; results[2*(iMax+1)+1]=corr[index+i]; } else { // y=ax^2+bx+c, x==0 for the middle point, c=y[0] already defined double b=0.5*(corr[index+i+1]-corr[index+i-1]); double a=0.5*(corr[index+i+1]+corr[index+i-1])-c; // negative double x=-0.5*b/a; results[2*(iMax+1)]= i+x-size/2+shift; results[2*(iMax+1)+1]=a*x*x+b*x+c; } iMax++; } } } // now sort the maximums if (orderMaximums) { if (debugLevel>2) System.out.println("correlationResults(): reordering maximums"); boolean ordered =false; while (!ordered){ ordered=true; for (int i=0; i<(numMax-1);i++) { if (debugLevel>2) System.out.println("correlationResults(): results["+(2*i+3)+"]="+results[2*i+3]+ ", results["+(2*i+5)+"]="+results[2*i+5]); if (results[2*i+3]2) System.out.println("correlationResults(): swapping max "+i+" and "+(i+1)); double tmp=results[2*i+3]; results[2*i+3] = results[2*i+5]; results[2*i+5]=tmp; tmp=results[2*i+2]; results[2*i+2] = results[2*i+4]; results[2*i+4]=tmp; } } } } return results; } public double [][] collectDisparityFromTiles( int debugLevel){ double [][] disparity=new double [6][this.mapWidth*this.mapHeight]; for (int n=0;nthis.mapHeight) yMaxLim=this.mapHeight; if (xMaxLim>this.mapWidth) xMaxLim=this.mapWidth; if (debugLevel>1) System.out.println("collectDisparityFromTiles(): this.tilesSize="+this.tilesSize+",yMinLim="+yMinLim+ ", yMaxLim="+yMaxLim+", xMinLim="+xMinLim+", xMaxLim="+xMaxLim); for (int iY=yMinLim;iY=0;level--){ int size = (this.tilesSize>>level); double [][] tile=this.tiles0[level][tY/size][tX/size]; if (tile!=null){ int index=iY*this.mapWidth+iX; disparity[2][index]=tile[0][0]; disparity[3][index]=tile[0][1]; // contrast background disparity[4][index]=tile[tile.length-1][0]; // last - FG if available, if not - BG disparity[5][index]=tile[tile.length-1][1]; // contrast *-ground disparity[0][index]=(disparity[3][index]>disparity[5][index])?disparity[2][index]:disparity[4][index]; disparity[1][index]=(disparity[3][index]>disparity[5][index])?disparity[3][index]:disparity[5][index]; break; } } } } return disparity; } // just temporary to visualize tiles - actual disparity will be processed diffirently public double [][] showDisparityFromTiles( int debugLevel){ double [][] disparity=new double [8][this.mapWidth*this.mapHeight]; for (int n=0;nthis.mapHeight) yMaxLim=this.mapHeight; if (xMaxLim>this.mapWidth) xMaxLim=this.mapWidth; if (debugLevel>1) System.out.println("collectDisparityFromTiles(): this.tilesSize="+this.tilesSize+",yMinLim="+yMinLim+ ", yMaxLim="+yMaxLim+", xMinLim="+xMinLim+", xMaxLim="+xMaxLim); for (int side=0;side=0;level--){ int size = (this.tilesSize>>level); double [] tile=this.tiles[side][self][level][tY/size][tX/size]; if (tile!=null){ // find single maximal value double maxV=0; int maxI=-1; for (int i=0;imaxV){ maxI=i; maxV=tile[i]; } if (maxI>=0){ double d=maxI; double c=tile[maxI]; if ((maxI>0) && (maxI<(tile.length-1) && (tile[maxI-1]>0.0) && (tile[maxI+1]>0.0))){// do not interpolate if 0.0 (masked out) // y=ax^2+bx+c, x==0 for the middle point, c=y[0] already defined double b=0.5*(tile[maxI+1]-tile[maxI-1]); double a=0.5*(tile[maxI+1]+tile[maxI-1])-c; // negative double x=-0.5*b/a; d+=x; // modify position c+=a*x*x+b*x; // value at max } int index=iY*this.mapWidth+iX; disparity[4*self+side ][index]=d+this.minDisparity; // position disparity[4*self+side+2][index]=c; // value (contrast) } break; } } } } } return disparity; } public float [][] createAmbiguityMaps( int side, double weightSobel, double weightCb, double weightCr, double noiseLev, boolean removePeriodic, double minPeriod, double minZeroAuto, double minPeriodContrast, double minAbsoluteFraction, double blurMaskSigma, double scale, int debugLevel){ //TODO: *********** here combine with cross-correlation with auto correlation, // int self=0; int numSamples= this.maxDisparity-this.minDisparity+1; if (this.ambiguityMap==null){ this.ambiguityMap=new float[2][][]; for (int i=0;i0); if (removePeriodic)autoLine[iDisp]=fromTilesAuto[index]*fromPixelDiffAuto[index]*fromAlphaAuto[index]; // remove fromAlpha? = new double[numSamples]; } if (nonEmpty){ if (removePeriodic) { filteredLine=removePeriodic( line, // double [] imageCross, autoLine, //double [] imageAuto, minPeriod, minZeroAuto, minPeriodContrast, minAbsoluteFraction, blurMaskSigma, scale, debugLevel); if (filteredLine!=null) line=filteredLine; } int [] iMax={-1,-1}; double [] max={0.0,0.0}; double [] maxP={0.0,0.0}; int index=sectionY*this.mapWidth+iX; for (int nMax=0;nMaxmax[nMax]){ max[nMax]=line[iDisp]; iMax[nMax]=iDisp; } } else { // find local maximum not equal to absolute one for (int iDisp=0;iDispmax[nMax]) && (iDisp!=iMax[0])) { if (((iDisp==0) || (line[iDisp]>=line[iDisp-1])) && ((iDisp==(numSamples-1)) || (line[iDisp]>=line[iDisp+1]))){ max[nMax]=line[iDisp]; iMax[nMax]=iDisp; } } } if (max[nMax]<=0.0) break; maxP[nMax]=iMax[nMax]; if ((iMax[nMax]>0) && (iMax[nMax]<(numSamples-1))){ // y=ax^2+bx+c, x==0 for the middle point, c=y[0] already defined double b=0.5*(line[iMax[nMax]+1]-line[iMax[nMax]-1]); double a=0.5*(line[iMax[nMax]+1]+line[iMax[nMax]-1])-max[nMax]; // negative double x=-0.5*b/a; maxP[nMax]+=x; max[nMax]+= a*x*x+b*x; //+c) } maxP[nMax]+=this.minDisparity; if (maxP[nMax]<0) maxP[nMax]=0.0; // limit disparity to 0.0 this.ambiguityMap[side][2*nMax][index]= (float) maxP[nMax]; this.ambiguityMap[side][2*nMax+1][index]=(float) max[nMax]; } } } IJ.showProgress(sectionY,this.mapHeight-2); } IJ.showProgress(1.0); return this.ambiguityMap[side]; } public float [] initialResolveMaps( int side, double threshold, double minSecondFrac, double edgesBonus, int debugLevel){ if (this.resolvedMap==null){ this.resolvedMap=new float[2][]; for (int i=0;i0) System.out.println("initialResolveMaps("+side+", "+threshold+", "+minSecondFrac+", "+edgesBonus+")"); this.resolvedMap[side]=new float[this.mapWidth*this.mapHeight]; int numLayers=this.overlapImages.length/2; int iAlphaThis= (side==0)?0:numLayers; double maxBonus=0; for (int i=0;i1){ if (maxBonus0) System.out.println("getResolvedState("+side+", "+threshold+", "+minSecondFrac+", "+edgesBonus+")"); float [] resolvedState=new float[this.mapWidth*this.mapHeight]; int numLayers=this.overlapImages.length/2; int iAlphaThis= (side==0)?0:numLayers; double maxBonus=0; for (int i=0;i1){ if (maxBonus=(debugY-debugDelta))&&(iY<=(debugY+debugDelta))&& (iX>=(debugX-debugDelta))&&(iX<=(debugX+debugDelta)))?1:0); if (Float.isNaN(this.resolvedMap[side][index])){ // looking for the undefined pixels // int numDefinedNeighbors=0; // double [] neighbors=new double [dirs.length]; for (int dir=0;dir1){ System.out.println("resolveAmbiguityStep():minExistentAlpha="+minExistentAlpha+" minExistentStrength="+minExistentStrength+ " weightCb="+weightCb+" weightCr="+weightCr+ " maxToneDiff="+maxDisparityDifference+" minNewStrength="+minNewStrength); System.out.println("weights={"+weights[0]+", "+weights[1]+", "+weights[1]+"}"); } byte [] mods=new byte [this.resolvedMap[side].length]; for (int i=0;i=(debugY-debugDelta))&&(iY<=(debugY+debugDelta))&& (iX>=(debugX-debugDelta))&&(iX<=(debugX+debugDelta)))?1:0); if (this.resolvedMap[side][index]>=0.0){ for (int iDir0=0;iDir0=0.0){ if (debugLevel>2){ System.out.println("resolveAmbiguityStep(): iY="+iY+" iX="+iX+" iDir0="+iDir0+ " index="+index+" index1="+index1+" index2="+index2); } existentDisparities[iDir]=this.resolvedMap[side][index2]; int dispar=((int) Math.round(existentDisparities[iDir])); if (((index2 % this.mapWidth)+dispar)2){ System.out.println("resolveAmbiguityStep(): existentAlphas[iDir]="+existentAlphas[iDir]+" < "+minExistentAlpha); } continue; // too small: alpha } if (Float.isNaN(this.ambiguityMap[side][2][index2])) optionUsed[iDir]=0; else { optionUsed[iDir]= (Math.abs(this.resolvedMap[side][index2]-this.ambiguityMap[side][0][index2])< Math.abs(this.resolvedMap[side][index2]-this.ambiguityMap[side][2][index2]))?0:1; } existentStrength[iDir]=this.ambiguityMap[side][2*optionUsed[iDir]+1][index2]; if (existentStrength[iDir]2){ System.out.println("resolveAmbiguityStep(): existentStrength[iDir]="+existentStrength[iDir]+" < "+minExistentStrength); } continue; // too small: existentStrength } weightredToneDiff[iDir]=0.0; for (int compNum=0;compNum<3;compNum++){ double d=this.overlapImages[iAlphaThis+compNum+1][index2]- // parent point this.overlapImages[iAlphaThis+compNum+1][index1] ;// this, new point weightredToneDiff[iDir]+=weights[compNum]*d*d; } weightredToneDiff[iDir]=Math.sqrt(weightredToneDiff[iDir]); if (weightredToneDiff[iDir]>maxToneDiff){ if (debugLevel>2){ System.out.println("resolveAmbiguityStep(): weightredToneDiff[iDir]="+weightredToneDiff[iDir]+" > "+maxToneDiff); } continue; // too large: weightredToneDiff } // both disparities should be present for the new point optionNew[iDir]= (Math.abs(this.resolvedMap[side][index2]-this.ambiguityMap[side][0][index1])< Math.abs(this.resolvedMap[side][index2]-this.ambiguityMap[side][2][index1]))?0:1; disparityDiff[iDir]=Math.abs(this.resolvedMap[side][index2]- this.ambiguityMap[side][2*optionUsed[iDir]][index1]); if (disparityDiff[iDir]>maxDisparityDifference){ if (debugLevel>2){ System.out.println("resolveAmbiguityStep(): disparityDiff[iDir]="+disparityDiff[iDir]+" > "+maxDisparityDifference); } continue; // too large: disparityDiff } newStrength[iDir]= this.ambiguityMap[side][2*optionUsed[iDir]+1][index1]; if (newStrength[iDir]2){ System.out.println("resolveAmbiguityStep(): newStrength[iDir]="+newStrength[iDir]+" > "+minNewStrength); } continue; // too small: newStrength } newOptions[optionNew[iDir]]=true; } } // for (int iDir=0;iDir1){ System.out.println("resolveAmbiguityStep():iY="+iY+" iX="+iX+" index1="+index1+" iDir0="+iDir0+ " different parents suggested different disparities options, both match"); } mods[index1]=3; // disable } else if (newOptions[0]) mods[index1]=1; // use first disparity else if (newOptions[1]) mods[index1]=2; // use second disparity else mods[index1]=3; // disable second disparity if (debugLevel>2){ System.out.println("resolveAmbiguityStep(): newOptions[0]="+newOptions[0]+ " newOptions[1]="+newOptions[1]+ " setting mod["+index1+"] (X="+(index1%this.mapWidth)+" Y="+(index1 / this.mapWidth)+ " to "+mods[index1]); } } } } } } // Apply all changes; int numFailed=0; int numResol=0; for (int i=0;i0) && (mods[i]<3)){ this.resolvedMap[side][i]=(mods[i]==1)?this.ambiguityMap[side][0][i]:this.ambiguityMap[side][2][i]; numResol++; } else if (mods[i]==3){ numFailed++; } } int [] result={numResol,numFailed}; return result; } /** * Combine pixel matching and correlation into disparity map * @param side 0 - left image, 1 - right * @param self 0 - with other image, 1 - with self (to detect periodic patterns) * @param weightCb weight of the image Cb component as fraction of Y * @param weightCr weight of the image Cr component as fraction of Y * @param noiseLev pixel noise as a fraction of the full scale (1.0) * @param debugLevel Debug level * @return array of the same format as original image */ public double [] getFrameDisparity( int side, int self, double weightSobel, double weightCb, double weightCr, double noiseLev, int debugLevel){ int numSamples= this.maxDisparity-this.minDisparity+1; double [] map=new double [this.mapWidth*this.mapHeight]; for (int i=0;i0); } if (nonEmpty){ int iMax=-1; double max=0.0; for (int iDisp=0;iDispmax){ max=line[iDisp]; iMax=iDisp; } if (max>0.0){ double maxP=iMax; if ((iMax>0) && (iMax<(numSamples-1))){ // y=ax^2+bx+c, x==0 for the middle point, c=y[0] already defined double b=0.5*(line[iMax+1]-line[iMax-1]); double a=0.5*(line[iMax+1]+line[iMax-1])-max; // negative double x=-0.5*b/a; maxP+=x; } maxP+=this.minDisparity; if (maxP<0) maxP=0.0; // limit disparity to 0.0 map[sectionY*this.mapWidth+iX]=maxP; } } } IJ.showProgress(sectionY,this.mapHeight-2); } IJ.showProgress(1.0); return map; } /** * Generate "cross-section" of the tiles data * @param sectionY Position of the section * @param side 0 - left image, 1 - right * @param debugLevel Debug level * @return 2d packed array - contrast of correlation for each disparity first, each pixel - second */ public double[] tilesSection( int sectionY, int side, int self, // 1 - autocorrelation int debugLevel){ int sectionLength=this.mapWidth; int numSamples= this.maxDisparity-this.minDisparity+1; double [] result = new double[sectionLength*numSamples]; for (int i=0;ithis.mapWidth) xMaxLim=this.mapWidth; int tY=sectionY-this.tilesY0; for (int iX=xMinLim;iX=0;level--){ size = (this.tilesSize>>level); tile=this.tiles[side][self][level][tY/size][tX/size]; if (tile!=null) break; } if (tile!=null){ int index=numSamples*iX; for (int i=0;i=0) && (iXOther edgeList=new ArrayList(10000); Integer Index; // int [] dirsI={0,1,1+imageWidth,imageWidth,-1+imageWidth, -1, -1 -imageWidth, -imageWidth, 1-imageWidth}; int [][]dirs={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; // x,y double [] image=imageSrc; long startTime = System.nanoTime(); if (alpha!=null){ image=imageSrc.clone(); for (int i=0;i=thresholdHigh){ edges[Index]=true; edgeList.add(Index); } } if (debugLevel>0){ System.out.println("Created initial list, length="+edgeList.size()+" at "+ IJ.d2s(0.000000001*(System.nanoTime()-startTime),3)); } int maxLength=0; int updateCount=updateCounts; while (edgeList.size()>0) { Index=edgeList.remove(0); // faster will be to use get()? int x=Index%imageWidth; int y=Index/imageWidth; for (int dir=0;dir=0) && (x1=0) && (y1=thresholdLow)){ edges[Index]=true; edgeList.add(Index); } } } if (showProgress){ if (maxLength0){ System.out.println("Thresholded image at "+ IJ.d2s(0.000000001*(System.nanoTime()-startTime),3)); } return edges; } //TODO: make an approximate version, that after making first N steps is looking when the parallel branches will come // as close as 1 orthogonal (not diagonal) pixel apart. THen calculate lengths and areas (adding missing connection) private int commonAncestor( int index1, int index2, int [][] topology, double [] timeToReach, byte [] parents, double [][][] node,// to find the best parent if there are many of them int debugLevel){ double ttr1,ttr2; int pDir; if (debugLevel>2) System.out.println("\n---commonAncestor("+index1+", "+index2+", ...):"); while ((index1!=index2) && (index1>=0) && (index2>=0)) { ttr1=timeToReach[index1]; ttr2=timeToReach[index2]; if (ttr1>ttr2){ if (node[index1]!=null) pDir=(int) node[index1][8][0]; else for (pDir=0;pDir<8;pDir++) if ((parents[index1]& (1<2) System.out.print (" parents["+index1+"]="+String.format("0x%02x",parents[index1])+" pDir="+pDir); index1=topology[index1][pDir]; if (debugLevel>2) System.out.println (" index1="+index1); } else { if (node[index2]!=null) pDir=(int) node[index2][8][0]; else for (pDir=0;pDir<8;pDir++) if ((parents[index2]& (1<2) System.out.print (" parents["+index2+"]="+String.format("0x%02x",parents[index2])+" pDir="+pDir); index2=topology[index2][pDir]; if (debugLevel>2) System.out.println (" index2="+index2); } } if (debugLevel>2) System.out.println( ((index1==index2)?index1:-1)+ " => (index1="+index1+", index2="+index2); return (index1==index2)?index1:-1; } //TODO: enhance nodes by "re-routing" to the common point (first run wave from the approximate center for some distance, then go back and find the point // with the smallest sum of times from each of the ends public void thinEdge( ArrayList edgeList, double [] image, boolean [] bitmapAccumulator, // array were the edges segment will be drawn (over existent ones) int imageWidth, int minCycleArea, // minimal area of the closed loop to keep int endRadius, //TODO: - other parameters to trim short branches and detect ends? boolean showProgress, float [][] dbgImage, // should have length of 7 (or null) int debugLevel0 ){ // int updateCounts=10; // int debugXMin=140; // int debugXMax=155; // int debugYMin=60; // int debugYMax=75; int debugLevel=debugLevel0; // int maxLength=0; // int updateCount=updateCounts; int imageHeight=image.length/imageWidth; int topologySize=edgeList.size(); int [][] topology = new int [topologySize][8]; int [][]dirs={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; // x,y double [] distance={1.0,Math.sqrt(2.0),1.0,Math.sqrt(2.0),1.0,Math.sqrt(2.0),1.0,Math.sqrt(2.0)}; // Next code is extremely slow if (debugLevel>1) System.out.println("thinEdge():0"); /* for (int i=0;i=0) && (x1=0) && (y1=0) && (x1=0) && (y11) System.out.println("thinEdge()"); double [][][] node = new double [topologySize][][]; // all cells but nodes have null here, nodes have {value, areas, lengths} triplets, the last row [8] - best parent for (int i=0;i1) System.out.println("thinEdge():1"); if (debugLevel>2) { System.out.println("Topology:"); for (int i=0;i<1000;i++) { System.out.print(String.format("%03d:" , i)); for (int j=0;j<8;j++) System.out.print(String.format(" %05d" , topology[i][j])); System.out.println(" X="+pixelX[i]+" Y="+pixelY[i]); } } //TODO: find a better start (i.e. run a wave from 0, but that still can result in a point on a circle) Integer start=0; timeToReach[0]=1.0; // ArrayList waveList=new ArrayList(topologySize); waveList.add(start); int processedCells=0; while (waveList.size()>0) { double min=timeToReach[waveList.get(0)]; int earliest=0; for (int i=1;itimeToReach[wi]){ earliest=i; min=timeToReach[wi]; } } Integer waveIndex=waveList.remove(earliest); debugLevel=debugLevel0+(((pixelX[waveIndex]>=this.debugXMin) && (pixelX[waveIndex]<=this.debugXMax) && (pixelY[waveIndex]>=this.debugYMin) && (pixelY[waveIndex]<=this.debugYMax))?1:0); if (debugLevel>2) System.out.println("\n>>>> pixelX["+waveIndex+"]="+pixelX[waveIndex]+" pixelY["+waveIndex+"]="+pixelY[waveIndex]); for (int dir=0;dir<8;dir++){ Integer newIndex=topology[waveIndex][dir]; if (newIndex>=0){ double newValue=2.0*distance[dir]/(speed[waveIndex]+speed[newIndex])+timeToReach[waveIndex]; int newArea=pathArea[waveIndex]+ pixelY[waveIndex]*(pixelX[newIndex]-pixelX[waveIndex])- pixelX[waveIndex]*(pixelY[newIndex]-pixelY[waveIndex]); short newLength=(short) (pathLength[waveIndex]+1); int dirToParent=((dir+4)%8); byte parentMask=(byte)(1<2) System.out.print("dir="+dir+" dirToParent="+dirToParent+String.format(" parentMask=0x%x",parentMask)+ " newIndex: pixelX["+newIndex+"]="+pixelX[newIndex]+" pixelY["+newIndex+"]="+pixelY[newIndex]); if (timeToReach[newIndex]==0.0){ // new cell - fill it timeToReach[newIndex]=newValue; parents[newIndex]=parentMask; pathArea[newIndex]=newArea; pathLength[newIndex]=newLength; waveList.add(newIndex); processedCells++; if (showProgress)IJ.showProgress(processedCells,topologySize); if (debugLevel>2) System.out.println( " --NEW CELL--"); } else { boolean improve=timeToReach[newIndex]>newValue; // as we take the smallest value from the list, all cells that can be improved do not have their children yet. But may have multiple parents if (improve){ int cycleWithBestArea=newArea-pathArea[newIndex]; if (cycleWithBestArea<0) cycleWithBestArea=-cycleWithBestArea; int commonIndex=commonAncestor( waveIndex, newIndex, topology, timeToReach, parents, node,// to find the best parent if there are many of them debugLevel); cycleWithBestArea-=(newLength+pathLength[newIndex]-2*(1+pathLength[commonIndex]));// should be 0 if the paths have no gaps - verify? if (debugLevel>2) System.out.print( " -- OLD CELL, IMPROVE, cycleWithBestArea="+cycleWithBestArea+" newArea="+newArea+" pathArea["+newIndex+"]="+pathArea[newIndex]+ " newLength="+newLength+" pathLength["+newIndex+"]="+pathLength[newIndex]+" common: pathLength["+commonIndex+"]="+pathLength[commonIndex]); if (cycleWithBestArea<=minCycleArea){ // replace the only or one of the older parents if (node[newIndex]==null){ // just replace, no need to add as it is (still) in the list parents[newIndex]=parentMask; } else { // already has multiple parents - remove those that do not qualify cycles criteria parents[newIndex]|=parentMask; // add new parent node[newIndex][8][0]=dirToParent; } } else { // if (cycleWithBestArea<=minCycleArea) - add cycle if (debugLevel>2) System.out.print( " -- OLD CELL, WORSE--"); if (node[newIndex]==null){ if (debugLevel>2) System.out.print( " -- not a node, setting a new node "); node[newIndex]=new double [9][3]; int pDir=0; for (int i=0;i<8;i++) if ((parents[newIndex] &(1<2) System.out.print( " -- setting parent of a node, pDir= "+pDir+ ", saving old values: ttr="+IJ.d2s(timeToReach[newIndex],3)+" area="+IJ.d2s(pathArea[newIndex],3)+" length="+IJ.d2s(pathLength[newIndex],3) ); // Save the only data set to the parents data set node[newIndex][pDir][0]=timeToReach[newIndex]; node[newIndex][pDir][1]=pathArea[newIndex]; node[newIndex][pDir][2]=pathLength[newIndex]; // } else { // if (node[newIndex]==null) } // if (node[newIndex]==null) parents[newIndex]|=parentMask; node[newIndex][8][0]=dirToParent; if (debugLevel>2) System.out.println(String.format(" Set node[%d][8][0]=%d", newIndex,dirToParent)); } // parents[newIndex] already has new (this) parent if (debugLevel>2) System.out.println(); timeToReach[newIndex]=newValue; pathArea[newIndex]=newArea; pathLength[newIndex]=newLength; if (node[newIndex]!=null){ // save the new data to node array, check/remove old parent(s if (debugLevel>2) System.out.print( " -- node is not null, saving new values to node["+newIndex+"]["+dirToParent+"] "); node[newIndex][dirToParent][0]=newValue; node[newIndex][dirToParent][1]=newArea; node[newIndex][dirToParent][2]=newLength; for (int pDir=0;pDir<8;pDir++) if ((pDir!=dirToParent) && ((parents[newIndex] &(1<2) System.out.println( " Comparing paths in the node, cycleWithBestArea="+cycleWithBestArea+" newArea="+newArea+ " node["+newIndex+"]["+pDir+"][1]="+node[newIndex][pDir][1]+ " newLength="+newLength+" node["+newIndex+"]["+pDir+"][2]="+node[newIndex][pDir][2]); if (cycleWithParentArea2) System.out.print( " Removed parent "+pDir+", was "+String.format("0x%02x",parents[newIndex])); parents[newIndex] &= ~(1<2) System.out.print( " now: "+String.format("0x%02x",parents[newIndex])); } } // That probably should not happen remove - number of parents could get down to 1: int numParents=0; for (int pDir=0;pDir<8;pDir++) if ((parents[newIndex] &(1<2) System.out.println( " Now there are "+numParents+" parents fro the "+newIndex); if (numParents<2) { if (debugLevel>0){ System.out.println("Removing parents array from former node at X="+pixelX[newIndex]+", Y="+pixelY[newIndex]); } node[newIndex]=null; } } } else {// if (improve). Maybe we've got a valid cycle, even if not came here first? if (debugLevel>2) System.out.print( " No improvement for "+newIndex+" --> "); if (node[newIndex]==null){ if (debugLevel>2) System.out.print( " Node did not exist"); int cycleWithBestArea=newArea-pathArea[newIndex]; if (cycleWithBestArea<0) cycleWithBestArea=-cycleWithBestArea; int commonIndex=commonAncestor( waveIndex, newIndex, topology, timeToReach, parents, node,// to find the best parent if there are many of them debugLevel); cycleWithBestArea-=(newLength+pathLength[newIndex]-2*(1+pathLength[commonIndex]));// should be 0 if the paths have no gaps - verify? if (debugLevel>2) System.out.println( " cycleWithBestArea="+cycleWithBestArea+" newArea="+newArea+" pathArea["+newIndex+"]="+pathArea[newIndex]+ " newLength="+newLength+" pathLength["+newIndex+"]="+pathLength[newIndex]+" common: pathLength["+commonIndex+"]="+pathLength[commonIndex]); if (cycleWithBestArea>minCycleArea){ // for most (no-node) pixels will fail if (debugLevel>2) System.out.print( " cycleWithBestArea> "+minCycleArea+", old parents="+String.format("0x%02x ", parents[newIndex])); int pDir=0; for (int i=0;i<8;i++) if ((parents[newIndex] &(1<2) System.out.println( String.format(" new mask =0x%02x new parents[%d]=0x%02x ", parentMask, newIndex, parents[newIndex])); node[newIndex]=new double [9][3]; if (debugLevel>2) System.out.println( String.format("Saved node[%d][%d] and node[%d][8][0]=%d", newIndex, pDir,newIndex, pDir)); // Save the only data set to the parents data set node[newIndex][pDir][0]=timeToReach[newIndex]; node[newIndex][pDir][1]=pathArea[newIndex]; node[newIndex][pDir][2]=pathLength[newIndex]; node[newIndex][8][0]=pDir; // direction to the best (old) parent if (debugLevel>2) System.out.println( String.format("Saved node[%d][%d]", newIndex, dirToParent)); // Save new parent data node[newIndex][dirToParent][0]=newValue; node[newIndex][dirToParent][1]=newArea; node[newIndex][dirToParent][2]=newLength; } // if (cycleWithBestArea>minCycleArea) - do not do anything } else { // if (node[newIndex]==null){ - adding more to the same node - is that likely? if (debugLevel>2) System.out.print( " Node existed, adding data to direction "+dirToParent); parents[newIndex]|=parentMask; // add new parent (we may remove it later node[newIndex][dirToParent][0]=newValue; node[newIndex][dirToParent][1]=newArea; node[newIndex][dirToParent][2]=newLength; if (debugLevel>2) System.out.println( String.format("Saved node[%d][%d] , now parents[%d]=0x%02x", newIndex, dirToParent, newIndex,parents[newIndex])); for (int pDir=0;pDir<8;pDir++) if ((pDir!=dirToParent) && ((parents[newIndex] & (1<2) System.out.println( " Comparing paths in the node with all parents, cycleWithBestArea="+cycleWithParentArea+" newArea="+newArea+ " node["+newIndex+"]["+pDir+"][1]="+node[newIndex][pDir][1]+ " newLength="+newLength+" node["+newIndex+"]["+pDir+"][2]="+node[newIndex][pDir][2]+" common: pathLength["+commonIndex+"]="+pathLength[commonIndex]); if (cycleWithParentArea=0) } } if (showProgress)IJ.showProgress(1.0); // May be a good idea to display wavefront image here for debugging? // now we can remove extra pixels if (dbgImage!=null){ // float [][]dbgImage=new float [7][image.length]; for (int n=0;n< dbgImage.length;n++) for (int i=0;i1) { System.out.println("Topology:"); for (int i=0;i<1000;i++) { System.out.print(String.format("%03d:" , i)); for (int j=0;j<8;j++) System.out.print(String.format(" %05d" , topology[i][j])); System.out.println(String.format(" X= %04d Y=%04d timeToReach=%f parents=0x%02x pathArea=%d pathLength=%d", pixelX[i], pixelY[i],timeToReach[i],parents[i],pathArea[i],pathLength[i])); } } } // now trim - first pass waveList=new ArrayList(10000); ArrayListendsList=new ArrayList(10000); byte [] stageBytes=new byte[topologySize]; for (int i=0;i1) &&(numParents>1)) { System.out.println(String.format("Considering cell %05d with %d parents, parents[%05d]=0x%02x", index, numParents, index,parents[index])); // continue; // multiple parents - keep it } boolean hasGreater=false; boolean hasChildren=false; for (int dir=0;dir<8;dir++){ int childIndex=topology[index][dir]; if (childIndex>=0){ int dirToParent=(dir+4)%8; if ((parents[childIndex]& (1<timeToReach[index]) hasGreater=true; if (hasGreater && hasChildren) break; } } if (!hasGreater && !hasChildren){ // try farther endsList.clear(); endsList.add(new Integer(index)); int listPos=0; for (int step=0; !hasGreater && (step=0) && (stageBytes[childIndex]==0)){ if (timeToReach[childIndex]>timeToReach[index]){ hasGreater=true; break; } else { endsList.add(childIndex); stageBytes[childIndex]=1; } } } } } while (endsList.size()>0){ Integer Index=endsList.remove(0); stageBytes[Index]=0; } } if (hasGreater && !hasChildren){ // trim waveList.add(new Integer(index)); if (debugLevel>1) System.out.println("Adding "+index+" for removal on the initial pass X="+pixelX[index]+", Y="+pixelY[index]+" numParents="+numParents); if (parents[index]==0){ System.out.println("BUG at X="+pixelX[index]+", Y="+pixelY[index]+" - parents["+index+"]=0"); } } } stageBytes=null; while (waveList.size()>0){ int index=waveList.remove(0); // multiple parents is not an excuse for survival int parentIndex=-1; if (parents[index]==0){ System.out.println("2: BUG at X="+pixelX[index]+", Y="+pixelY[index]+" - parents["+index+"]=0"); } int hadParents=parents[index]; parents[index]=0; for (int pDir=0;pDir<8;pDir++) if ((hadParents & (1<1) System.out.println(String.format("NOT adding for removal %05d X=%05d, Y=%05d child-X=%05d, child-Y=%05d parents[%05d]=0x%02x", parentIndex, pixelX[parentIndex],pixelY[parentIndex],pixelX[index],pixelY[index],index, hadParents)+ " hasGreater="+hasGreater+" hasChildren="+hasChildren); } } } if (dbgImage!=null){ // float [][]dbgImage=new float [7][image.length]; for (int i=0;i edgeList=new ArrayList(10000); int numEdge=0; for (int startIndex=0;startIndex=0) && (x1=0) && (y1 edgeList=new ArrayList(10000); edgeList.add(Index); edgesStage[Index]=false; int position=0; while (position=0) && (x1=0) && (y10){ System.out.println("testEdgeThinning(): list size="+edgeList.size()); } float [][]dbgImage=new float [7][image.length]; thinEdge( edgeList, image, null, imageWidth, minCycleArea, // minimal area of the closed loop to keep endRadius, //TODO: - other parameters to trim short branches and detect ends? showProgress, dbgImage, debugLevel ); return dbgImage; } public class EdgesSegmeniting{ public byte emptyCell=0; public byte badCell=1; public byte oldCell=2; public byte newCell=3; public int imageWidth; public int imageHeight; public int imageLength; public int [] iDirs4=null; // orthogonal only public int [] iDirs8=null; // diagonoa public int [][]dirs4={{1,0}, {0,1}, {-1,0}, {0,-1}}; // x,y public int [][]dirs8={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; // x,y public EdgesSegmeniting(){} public EdgesSegmeniting(int width,int height){setImageDimensions(width,height);} public void setImageDimensions( int width, int height){ this.imageWidth=width; this.imageHeight=height; this.imageLength=width*height; int [] iDirs4={1, this.imageWidth, -1, -this.imageWidth }; // orthogonal only int [] iDirs8={1, this.imageWidth+1, this.imageWidth, this.imageWidth-1, -1, -this.imageWidth-1, -this.imageWidth, -this.imageWidth+1}; // diagonal this.iDirs4=iDirs4; this.iDirs8=iDirs8; } public float [] distanceFromEdges( boolean [] edges, boolean [] alphaMask,// or null int imageWidth, int numPasses, int debugDirections, int debugLevel ){ setImageDimensions(imageWidth,edges.length/imageWidth); return distanceFromEdges( edges, alphaMask, numPasses, debugDirections, debugLevel ); } public float [] distanceFromEdges( boolean [] pureEdges, boolean [] alphaMask,// or null int numPasses, int debugDirections, int debugLevel ){ boolean [] edges=pureEdges; if (alphaMask!=null){ edges=pureEdges.clone(); for (int i=0;i0){ if ((debugDirections & 16)!=0) distanceFromEdgesLine( edges, stage, // should be initialized with large enough number x, //int x0, 0, //int y0, 1, //int dx, // 0/=1 1, //int dy, // -1, 0, +1 debugLevel); if ((debugDirections & 32)!=0) distanceFromEdgesLine( edges, stage, // should be initialized with large enough number x, //int x0, this.imageHeight-1, //int y0, 1, //int dx, // 0/=1 -1, //int dy, // -1, 0, +1 debugLevel); } } } return stage; } private void distanceFromEdgesLine( boolean [] edges, float [] stage, // should be initialized with large enough number int x0, int y0, int dx, // 0/=1 int dy, // -1, 0, +1 int debugLevel ){ if (((dx==0) && (dy==0)) || (dx<-1) || (dy<-1) || (dx>1) || (dy>1)) { String msg="Illegal values for dx="+dx+" and/or dy "+dy; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } if ((x0<0) || (y0<0) || (x0>=this.imageWidth) || (y0>=this.imageHeight)){ String msg=String.format("Illegal values for x0=%d (should be 0 <= x0 < %d) and/or y0=%d (should be 0 <= y0 <%d ",x0, this.imageWidth, y0, this.imageHeight); IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } double stepDistance=Math.sqrt(dx*dx+dy*dy); int length; if (dx!=0){ if (dx>0) length= this.imageWidth-x0; else length=x0; } else length=this.imageHeight; if (dy!=0){ if (dy>0) { if ((this.imageHeight-y0)0) && isDiagonal) { if (edges[index-xStep] || edges[index-yStep])edgeDistance=0.5*stepDistance; } if (stage[index]>edgeDistance) stage[index]=(float) edgeDistance; else edgeDistance=stage[index]; edgeDistance+=stepDistance; index+=indexStep; } // reverse pass; indexStep=-indexStep; xStep=-xStep; yStep=-yStep; index+=indexStep; // just one step back edgeDistance=stage[index]; for (int i=0;i0) && isDiagonal) { if (edges[index-xStep] || edges[index-yStep])edgeDistance=0.5*stepDistance; } if (stage[index]>edgeDistance) stage[index]=(float) edgeDistance; else edgeDistance=stage[index]; edgeDistance+=stepDistance; index+=indexStep; } } public int [] extractEdgesAreas( boolean gapsNotEdges, double threshold, float [] edgeDistance, int imageWidth, Rectangle [][] bounds0, // null or one element Rectangle[] - will return bounding rectangle for each of the edge areas boolean showProgress, int debugLevel ) { setImageDimensions(imageWidth,edgeDistance.length/imageWidth); return extractEdgesAreas( gapsNotEdges, threshold, edgeDistance, bounds0, // null or one element Rectangle[] - will return bounding rectangle for each of the edge areas showProgress, debugLevel); } public int [] extractEdgesAreas( boolean gapsNotEdges, double threshold, float [] edgeDistance, Rectangle [][] bounds0, // null or one element Rectangle[] - will return bounding rectangle for each of the edge areas boolean showProgress, int debugLevel ){ int [] edgesAreas=new int [this.imageLength]; for (int i=0;i edgeList=new ArrayList(10000); int numEdge=0; int xMin=2,xMax=this.imageWidth-3,yMin=2,yMax=this.imageHeight-3; // to avoid checking when following paths around (during edges vacuum) for (int startIndex=0;startIndex1) System.out.println("Isolating edge group "+(numEdge)+"..."); while ((startIndexthreshold)) || ((startIndex%this.imageWidth)xMax) || ((startIndex/this.imageWidth)yMax) )) startIndex++; Integer Index=startIndex; if (debugLevel>1)System.out.println("Start point: x="+(startIndex%this.imageWidth)+", y="+(startIndex/this.imageWidth)+ " (xMin="+xMin+" xMax="+xMax+" (yMin="+yMin+" yMax="+yMax+")"); if (startIndex0) { Index=edgeList.remove(0); int x=Index%this.imageWidth; int y=Index/this.imageWidth; for (int dir=0;dir=xMin) && (x1<=xMax) && (y1>=yMin) && (y1<=yMax)){ Index=x1+y1*this.imageWidth; if ( (edgesAreas[Index]==0) && (gapsNotEdges?(edgeDistance[Index]>threshold):(edgeDistance[Index]<=threshold))){ edgesAreas[Index]=numEdge; edgeList.add(Index); dbgLength++; } } } } if (debugLevel>1)System.out.println("Area size="+dbgLength); } } if (bounds0!=null){ Rectangle [] bounds=new Rectangle[numEdge-1]; for (int i=0;i0){ int d= edgesAreas[i]-1; int x=i%this.imageWidth; int y=i/this.imageWidth; if (x< bounds[d].x) bounds[d].x=x; if (y< bounds[d].y) bounds[d].y=y; if (x> bounds[d].width) bounds[d].width=x; if (y> bounds[d].height) bounds[d].height=y; } for (int i=0;i1)System.out.println("bounds["+i+"]: x="+bounds[i].x+", y="+bounds[i].y+", width="+bounds[i].width+", height="+bounds[i].height); } bounds0[0]=bounds; } if (showProgress) IJ.showStatus(""); return edgesAreas; } public ArrayList> createInitialEdgeAreaBorder( int [] edgeAreas, int edgeAreaNumber, int imageWidth, Rectangle bounds, // bounding rectangle to speed up search. Should not have y, x<2 and > appropriate dimensions boolean showProgress, int debugLevel ){ setImageDimensions(imageWidth,edgeAreas.length/imageWidth); return createInitialEdgeAreaBorder( edgeAreas, edgeAreaNumber, bounds, // bounding rectangle to speed up search. Should not have y, x<2 and > appropriate dimensions showProgress, debugLevel); } public ArrayList> createInitialEdgeAreaBorder( // never returns int [] edgeAreas, int edgeAreaNumber, Rectangle bounds, // bounding rectangle to speed up search. Should not have y, x<2 and > appropriate dimensions boolean showProgress, int debugLevel ){ int minDebugLevel=3; int length=edgeAreas.length; boolean [] stage=new boolean[length]; int margins=2; int yMin=bounds.y-margins, yMax=bounds.y+bounds.height+margins,xMin=bounds.x-margins, xMax=bounds.x+bounds.width+margins; for (int y=yMin;y> boundariesList=new ArrayList>(100); for (int startIndex=0;startIndex<(length-1);) { // if (showProgress) IJ.showStatus("Isolating edge group "+(numEdge)+"..."); while ((startIndex<(length-1)) && (stage[startIndex] || // boundary already marked (edgeAreas[startIndex+1]!=edgeAreaNumber) || // next does not fit (edgeAreas[startIndex]==edgeAreaNumber))) startIndex++; // this fits if (startIndex>=(length-1)) break; // Now this current does not belong to edgeArea, and the east neighbor - does int dir=3; // up ArrayList boundarysList=new ArrayList(10000); Integer Index=startIndex; int startDir=dir; dir=(startDir+1)%4; if ((edgeAreas[Index+this.iDirs4[dir]]!=edgeAreaNumber)) startDir=dir; else if ((edgeAreas[Index+this.iDirs4[startDir]]==edgeAreaNumber)){ dir=(startDir+3)%4; if ((edgeAreas[Index+this.iDirs4[dir]]!=edgeAreaNumber)) startDir=dir; else startDir=(startDir+2)%4; // turn back } // nothing matched - go on forward dir=startDir; if (debugLevel>=minDebugLevel) System.out.println("createInitialEdgeAreaBorder(,"+edgeAreaNumber+",..): startIndex="+startIndex+" startDir="+startDir); int debugNum=0; do { boundarysList.add(Index); stage[Index]=true; Index+=this.iDirs4[dir]; if (edgeAreas[Index]==edgeAreaNumber) break; // one-point border int dir1=(dir+1)%4; if ((edgeAreas[Index+this.iDirs4[dir1]]!=edgeAreaNumber)) dir=dir1; else if ((edgeAreas[Index+this.iDirs4[dir]]==edgeAreaNumber)){ dir1=(dir+3)%4; if ((edgeAreas[Index+this.iDirs4[dir1]]!=edgeAreaNumber)) dir=dir1; else dir=(dir+2)%4; // turn back } // nothing matched - go on forward if (debugLevel>=minDebugLevel){ System.out.println("createInitialEdgeAreaBorder(): Index="+Index+", dir="+dir+" x="+(Index%this.imageWidth)+" y=" +(Index/this.imageWidth)+ " (startIndex="+startIndex+" startDir="+startDir+" strat-x="+(startIndex%this.imageWidth)+" start-y=" +(startIndex/this.imageWidth)+")"); } if (Index==startIndex) debugNum++; if (debugNum>10){ String msg="Going circles 10 times"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } } while ((Index!=startIndex) || (dir!=startDir)); // follow border, mark boundary on stage boundariesList.add(boundarysList); } return boundariesList; } public int vacuumEdgeAreaBorder( double thresholdStart, // the same as for initial segmenting double thresholdFinal, // threshold (lower) for the shrank edge areas double thresholdStep, // decrease threshold between passes boolean repeatEachStep, // repeat each step until no new points boolean repeatLastStep, // repeat last step until no new points float [] edgeDistance, // array of distances from the nearest edge int [] edgeAreas, // or null. If not null, will zero out removed areas int edgeAreaNumber, // any if edgeAreas is null ArrayList> borders, // will be modified int imageWidth, Rectangle bounds, // bounding rectangle to speed up search. Should not have y, x<2 and > appropriate dimensions boolean showProgress, int debugLevel ){ // byte emptyCell=0; // byte badCell=1; // byte oldCell=2; // byte newCell=3; /// private enum cellTypes {EMPTY_CELL, BAD_CELL, OLD_CELL,NEW_CELL} int minDebugLevel=3; int totalNewCells=0; if (debugLevel>=minDebugLevel){ System.out.println("vacuumEdgeAreaBorder("+thresholdStart+","+thresholdFinal+","+thresholdStep+", [][] ,"+edgeAreaNumber+",<>,"+imageWidth+",,,"+debugLevel+")"); } int length=edgeAreas.length; // int imageHeight=length/imageWidth; byte [] stage=new byte[length]; int margins=2; int yMin=bounds.y-margins, yMax=bounds.y+bounds.height+margins,xMin=bounds.x-margins, xMax=bounds.x+bounds.width+margins; for (int y=yMin;y border= borders.get(numBorder); for (int i=0;i=minDebugLevel){ float [] fEdge=new float [length]; for (int i=0;iindex1)?((index2>(index1+1)?1:0)):((index2<(index1-1)?3:2)); index=index1+this.iDirs4[(startDir+1)%4]; if (stage[index]!=this.oldCell) break; // no newCells - only empty or bad } if (index<0){ String msg="all right-hand neighbors are OLD"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } // this.badCell - not currently used // find the start point for the new border segment if (stage[index]==this.badCell) { index=index1; // start with old cell - will have to come here again } else { // can only be a new cell here if (edgeDistance[index]<=threshold) { index=index1; // start with old cell - will have to come here again } else { int numNeibGroups=0; for (int dir8=0;dir8<8;dir8++) if ((stage[index+this.iDirs8[dir8]]=this.oldCell)) { numNeibGroups++; if (numNeibGroups>1) break; // no need to look more } if (numNeibGroups>1){ index=index1; // start with old cell - will have to come here again // no, can not mark it "bad" } } } // find start direction for the new border int dir=startDir; int blockedCell=-1; // none float blockedCellValue=Float.NaN; //starting from a new cell (even as the first move will likely be to the old cell) ensures the border will shrink here. // temoporarily blocking the blockedCell prevents "short-circuit" near the start cell. // using edgeDistance[] - no new comparisons will be needed if (index!=index1) { for (dir=startDir;dir!=((startDir+3)%4); dir=(dir+1)%4) if (stage[index+this.iDirs4[dir]]!=this.oldCell) break; if (dir==((startDir+3)%4)){ String msg="all old cells around index="+index+ " (x="+(index%this.imageWidth)+", y="+index/this.imageWidth+")"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } startDir=(dir+3)%4; // last old cell clockwise from the old start blockedCell=index+this.iDirs4[dir]; blockedCellValue=edgeDistance[blockedCell]; edgeDistance[blockedCell]=(float)(threshold-1.0); // temporarily block the first non-old cell by lovering threshold } // now we do not need old border segment list, can start again: int startIndex=index; border.clear(); badCells.clear(); Integer Index=startIndex; dir=startDir; if (debugLevel>=minDebugLevel){ System.out.println(" **** vacuumEdgeAreaBorder() startDir="+startDir+" startIndex="+startIndex+ " start-x="+(startIndex%imageWidth)+" start-y="+(startIndex/imageWidth)+ " blockedCell="+blockedCell+ " threshold="+threshold); } if (stage[Index]!=this.oldCell) stage[Index]=this.newCell; //*** java.lang.ArrayIndexOutOfBoundsException: -612 do { if (debugLevel>=minDebugLevel){ System.out.print(" --- dir="+dir+" Index="+Index+ " x="+(Index%imageWidth)+" y="+(Index/imageWidth)+ " stage[Index]="+stage[Index]+ " startDir="+startDir+" startIndex="+startIndex+ " start-x="+(startIndex%imageWidth)+" start-y="+(startIndex/imageWidth)+ " blockedCell="+blockedCell); } border.add(Index); // if (stage[Index]!=this.oldCell) stage[Index]=this.newCell; //*** java.lang.ArrayIndexOutOfBoundsException: -612 Index+=this.iDirs4[dir];// if (stage[Index]!=this.oldCell) stage[Index]=this.newCell; //*** java.lang.ArrayIndexOutOfBoundsException: -612 if (debugLevel>=minDebugLevel){ System.out.println(" === NEW Index="+Index+ " x="+(Index%imageWidth)+" y="+(Index/imageWidth)+ " stage[Index]="+stage[Index]); } int dir1=(dir+1)%4; for (dir1=(dir+1)%4;dir1!=(dir+2)%4; dir1=(dir1+3)%4){ // see if step in dir1 is permitted index2=Index+this.iDirs4[dir1]; if (stage[index2]>=this.oldCell) break; // old /new cell - OK, use it if ((stage[index2]==this.badCell) || // not yet used (edgeDistance[index2]<=threshold)) continue; // does not fit threshold criteria - try next // index2: still empty cell, fits criteria - make sure it doeas not break the link int numNeibGroups=0; for (int dir8=0;dir8<8;dir8++) if ((stage[index2+this.iDirs8[dir8]]=this.oldCell)) { if (debugLevel>=minDebugLevel) System.out.print(" "+dir8+":"+((dir8+1)%8)+" "); numNeibGroups++; if (numNeibGroups>1) break; // no need to look more } if (debugLevel>=minDebugLevel){ System.out.println("\nnumNeibGroups="+numNeibGroups+" index2="+index2+ " x="+(index2%imageWidth)+" y="+(index2/imageWidth)+" stage[index2]="+stage[index2]+" edgeDistance[index2]="+edgeDistance[index2]); } // if (numNeibGroups>1) continue; // using this cell will break link between edges, do not use it. if (numNeibGroups<2) break; // using this cell will not break link between edges, OK to use it. badCells.add(new Integer(index2)); stage[index2]=this.badCell; // do niot try it again in this pass } // now index2 - where to go, dir1-direction to go dir=dir1; } while ((Index!=startIndex) || (dir!=startDir)); // restore original value of the blocked cell if (blockedCell>=0) edgeDistance[blockedCell]=blockedCellValue; // mark new cells as old // restore bad cells int newCells=0; for (int i=0;i=minDebugLevel){ System.out.println("++++++++++++ New cells:"+newCells); // repeat untill no new cells? } } if (thisNewCells>0){ if (repeatEachStep || (repeatLastStep && ((threshold-thresholdStep) < thresholdFinal))) threshold+=thresholdStep; // will repeat the pass } } if (edgeAreas!=null){ for (int y=bounds.y; y<(bounds.y+bounds.height);y++) for (int x=bounds.x; x<(bounds.x+bounds.width);x++){ int index=y*this.imageWidth+x; if ((edgeAreas[index]==edgeAreaNumber) && (stage[index]==this.oldCell)) edgeAreas[index]=0; // erase it here } } return totalNewCells; } } public double [] sobelFilter( double [] srcImage, int width, double edgeSpreadOrtho, // not used double edgeSpreadDiagonal // not used ){ int height=srcImage.length/width; double p1,p2,p3,p4,p5,p6,p7,p8,p9,s1,s2; double [] sobelResult=new double [srcImage.length]; int upRight,downRight; int index; for (int iY=0;iY0.0) || (edgeSpreadDiagonal>0.0); // if (spread) for (int i=0;i>> weightedRMS="+weightedRMS+ " weightedDiff="+weightedDiff+ " result[iX*numSamples+iDispar]="+result[iX*numSamples+iDispar]+ " noiseLev="+noiseLev); } */ } } return result; } public double [] sectionPixelSobel( int sectionY, int side, int self, int debugLevel){ if (sectionY<1) sectionY=1; // to skip tests for the valid neighbors if (sectionY> (this.mapHeight-2)) sectionY=this.mapHeight-2; int sectionLength=this.mapWidth; int numSamples= this.maxDisparity-this.minDisparity+1; double [] result = new double[sectionLength*numSamples]; for (int i=0;i1) && (iXOther<(this.mapWidth-1))){ int indexThis= sectionY*this.mapWidth+iX; int indexOther=sectionY*this.mapWidth+iXOther; result[iX*numSamples+iDispar]=this.sobelY[side][indexThis]*this.sobelY[sideOther][indexOther]; } } return result; } public double[] removePeriodicSection( double [] imageCross, double [] imageAuto, double minPeriod, double minZeroAuto, double minPeriodContrast, double minAbsoluteFraction, double blurMaskSigma, double scale, int debugLevel){ int dbgMin=360,dbgMax=380; int sectionLength=this.mapWidth; int numSamples= this.maxDisparity-this.minDisparity+1; double [] result = new double[sectionLength*numSamples]; for (int i=0;i=dbgMin) && (iX<=dbgMax)) dbg+=1; if (dbg>1) System.out.print("iX="+iX+" >> "); double [] combined=removePeriodic( cross, auto, minPeriod, minZeroAuto, minPeriodContrast, minAbsoluteFraction, blurMaskSigma, scale, dbg); //debugLevel); if (combined!=null) cross=combined; index=iX*numSamples; for (int i=0;i2){ for (int i=0;i2) { System.out.println("removePeriodic(): autocorrelation at zero ("+auto[-this.minDisparity]+") <= "+minZeroAuto); } return null; } int minIndex=(int) minPeriod-this.minDisparity; int halfMinPeriod=(int) Math.ceil(minPeriod/2); double max=0.0; for (int i=minIndex; i<(numSamples-halfMinPeriod);i++) if (auto[i]>max) max=auto[i]; double threshold=minPeriodContrast*auto[-this.minDisparity]; // minimal threshold if (threshold<(max*minAbsoluteFraction)) threshold=max*minAbsoluteFraction; int iMax=-1; if (minIndex<1) minIndex=1; // looking for the first local max that fits boolean localMax; for (int i=minIndex;i<(numSamples-halfMinPeriod);i++) if (auto[i]>threshold){ localMax=true; for (int j=-halfMinPeriod; jauto[i]){ localMax=false; break; } if (localMax) { // also - above threshold iMax=i; break; } } if (iMax<0) { if (debugLevel>1) System.out.println("removePeriodic(): No qualifying period above "+minPeriod+ " detected, minIndex="+minIndex+" auto["+(-this.minDisparity)+"]="+auto[-this.minDisparity]); return null; // no maximum above minPeriod } max=auto[iMax]; /* // Make sure there are no local maximums between zero and this ? if (!(max>(minPeriodContrast*auto[-this.minDisparity]))){ if (debugLevel>1) System.out.println("removePeriodic(): contrast too low ("+max+"<="+minPeriodContrast*auto[-this.minDisparity]); return null; // contrast too low } */ // find minimal value between zero max and this new found one int iMin=0; double min= auto[-this.minDisparity]; for (int i=-this.minDisparity+1;i1) System.out.println("removePeriodic(): period ="+period+" auto["+(-this.minDisparity)+"]="+auto[-this.minDisparity]+ " max="+max+" min="+min+" iMin="+iMin); if (min<0) min=0.0; // change floor //double [] mask=cross.clone(); double [] mask=new double [cross.length]; double k=scale/auto[-this.minDisparity]; for (int i=0;i=0) && (cross[iSrc]>min))? (k*(cross[iSrc]-min)):0.0; } if (blurMaskSigma>0.0) { (new DoubleGaussianBlur()).blur1Direction( mask, numSamples, 1, blurMaskSigma, 0.01, true); } double [] result=cross.clone(); if (dbg) for (int i=0;iiMin) && (auto[i]>min))? (k*(auto[i]-min)):0.0; if (blurMaskSigma>0.0) { (new DoubleGaussianBlur()).blur1Direction( mask, numSamples, 1, blurMaskSigma, 0.01, true); } double [] result=cross.clone(); for (int i=period;i0) && (this.overlapImages[numLayers][index]>0)){ if (iy>maxY) maxY=iy; if (iymaxX) maxX=ix; if (ix0) System.out.println("No overlap"); return null; } int width= (maxX-minX+1); int height=(maxY-minY+1); this.tilesNHor=width/size; if ((this.tilesNHor*size)0.0) && (level>0)){ double [] parentTile=this.tiles[side][self][level-1][tileY/2][tileX/2]; // should never be null for (int i=0;i0.0){ tile[i]*=(1.0+parentFraction*parentTile[i]); } } if (neighborsFraction>0.0){ double [] neighbors=new double[tile.length]; int [] numUsed= new int[tile.length]; for (int i=0;i10){ System.out.println( " tmpTiles.length="+tmpTiles.length+ " tmpTiles[0.length="+tmpTiles[0].length+ " nTileY="+nTileY+ " nTileX="+nTileX+ " side="+side+ " self="+self+ " level="+level); if ((nTileX>=0) && (nTileX=0) && (nTileY0 to have same alpha==1.0 * @param minTileFraction minimal tile overlap fraction to process tile (will be squared in the corners of the tile map) * @param threadsMax Maximal number of concurrent threads to run * @param debugLevel Debug level * @return Generated tile (or null if no tile was generated) */ public double [] generateTile( int side, int self, int level, int tileX, int tileY, double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double contrastThreshold, double relativeStep, boolean probeAtLimits, boolean binaryAlpha, double minTileFraction, DoubleFHT doubleFHT, int threadsMax, int debugLevel){ int size=(this.tilesSize>>level); boolean isCorner=( ((tileY==0)||(tileY==(this.tiles[side][self][level].length))) && ((tileX==0)||(tileX==(this.tiles[side][self][level][0].length)))); if (isCorner) minTileFraction*=minTileFraction; int corrXC=this.tilesX0+tileX*size+size/2; int corrYC=this.tilesY0+tileY*size+size/2; double [] parentTile; if (level==0) parentTile=null; else { parentTile= this.tiles[side][self][level-1][tileY/2][tileX/2]; if (parentTile==null) return null; // no parent to this tile } if (debugLevel>2){ System.out.println("======> generateTile("+side+", "+self+", "+level+", "+tileX+", "+tileY+",...)"); } // first series of correlations using shift from the higher level int [] shifts = generateShiftsList( contrastThreshold, // double threshold, size, // int size, relativeStep, //double relativeStep, probeAtLimits, //boolean probeAtLimits, null, // int [] currentShifts, parentTile, //double [] prevCorrelation, debugLevel //int debugLevel ); if (shifts==null){ return null; } if (debugLevel>2){ System.out.print("Accumulating - first pass: "); } double [][] accumulatedCorrs=correlateMultipleShifts( side, self, size, corrXC, corrYC, shifts, // >=0; phaseCorrelationFraction, corrCbWeight, corrCrWeight, correlationHighPassSigma, correlationLowPassSigma, noiseNormalizationSignaY, noiseNormalizationSignaCbCr, binaryAlpha, minTileFraction, // decrease for corners doubleFHT, threadsMax, debugLevel); if (accumulatedCorrs==null){ if (debugLevel>2){ System.out.println("First pass got null"); } return null; } // additional correlations shifted according to the new results int [] newShifts=generateShiftsList( contrastThreshold, // double threshold, size, // int size, relativeStep, //double relativeStep, probeAtLimits, //boolean probeAtLimits, shifts, // int [] currentShifts, accumulatedCorrs[0], //parentTile, //double [] prevCorrelation, debugLevel //int debugLevel ); if (newShifts==null){ if (debugLevel>2){ System.out.println("First pass - all below threshold"); } return null; } if (newShifts.length>0){ // length==0 -> no new shifts to try if (debugLevel>2){ System.out.print("Accumulating - second pass: "); } double [][] additionalCorrs=correlateMultipleShifts( side, self, size, corrXC, corrYC, newShifts, // >=0; phaseCorrelationFraction, corrCbWeight, corrCrWeight, correlationHighPassSigma, correlationLowPassSigma, noiseNormalizationSignaY, noiseNormalizationSignaCbCr, binaryAlpha, minTileFraction, // decrease for corners doubleFHT, threadsMax, debugLevel); if (additionalCorrs!=null){ // combine accumulatedCorrs with additionalCorrs for (int i=0;i0.0) accumulatedCorrs[0][i]=(accumulatedCorrs[0][i]*accumulatedCorrs[1][i]+additionalCorrs[0][i]*additionalCorrs[1][i])/w; } } } this.tiles[side][self][level][tileY][tileX]=accumulatedCorrs[0]; return this.tiles[side][self][level][tileY][tileX]; } /** * Create new or additional list of the correlation shifts to try * @param threshold Minimal correlation contrast to grant * @param size Tile size (half of the correlation FFT size * @param relativeStep Maximal distance between correlation shifts relative to size (if distance from the new * shift and already calculated is less than that, new correlation will not be performed * @param probeAtLimits Add shift near the minimal and maximal shifts over threshold even if they are not local maximums * @param currentShifts Array of correlation shifts already calculated (or null) * @param prevCorrelation Array of previous correlation results (or null ) * @param debugLevel Debug level * @return array of non-negative correlation shifts to try (may be zero length if none are needed), null - all below threshold */ int[] generateShiftsList( double threshold, int size, double relativeStep, boolean probeAtLimits, int [] currentShifts, double [] prevCorrelation, int debugLevel ){ int minStep=1; // make it 2? int step=(int) Math.round(relativeStep*size); if (step2){ System.out.println("generateShiftsList(): prevCorrelation was null, returning initial list"); } return initialShifts; } int minShift=prevCorrelation.length,maxShift=0; boolean [] localMax=new boolean [prevCorrelation.length]; double maxV=0.0; for (int i=0;i< localMax.length;i++) { if (prevCorrelation[i]>=maxV) maxV=prevCorrelation[i]; if (prevCorrelation[i]>=threshold){ if (i>maxShift) maxShift=i; if (i=-this.minDisparity) && (i>0) && (prevCorrelation[i]>=prevCorrelation[i-1]) && (i<(prevCorrelation.length-1)) && (prevCorrelation[i]>prevCorrelation[i+1])); if ((i==-this.minDisparity) && (i<(prevCorrelation.length-1)) && (prevCorrelation[i]>prevCorrelation[i+1])) localMax[i]=true; // only if above limit } } if (minShift>maxShift) { if (debugLevel>2){ System.out.println("generateShiftsList(): All below threshold: maxV="+maxV+" threshold="+threshold+" (length="+localMax.length+")"); for (int i=0;i2){ System.out.print("generateShiftsList(): before probeAtLimits: "); for (int i=0;imaxShift) lshift=(maxShift+minShift)/2; if ((this.minDisparity+lshift)<0) lshift=-this.minDisparity; if (lshift=minShift) localMax[lshift]=true; if (debugLevel>2){ System.out.print("generateShiftsList(): after probeAtLimits: "); for (int i=0;i=0) && (j2){ System.out.print("generateShiftsList(): alreadyDone from old="); for (int i=0;i=0) && (j=0){ shifts[index++]=iShifts[i]+this.minDisparity; } return shifts; // may be zero length } /** * Correlate two images with different shifts (needed when shifts are large compared to FFT size) * and accumulate results into an array, starting with specified this.minDisparity * @param side - 0 - left image, 1 - right one * @param self - 0 - cross-correlate, 1 - auto-correlate * @param size - Tile size (half of the FFT size * @param corrXC Center of the correlation area on the first image, X * @param corrYC Center of the correlation area on the first image, Y * @param shifts Array of shifts to use for correlation (>=0) * @param phaseCorrelationFraction 1.0 - use pure phase correlation, 0.0 - just correlation * @param corrCbWeight relative weight of the Cb component in correlation (Y has weight of 1.0) * @param corrCrWeight relative weight of the Cr component in correlation (Y has weight of 1.0) * @param correlationHighPassSigma Correlation high-pass filter sigma, pixels in frequency domain * @param correlationLowPassSigma Correlation low-pass filter sigma, fraction of the frequency range * @param noiseNormalizationSignaY Noise normalization sigma for Y-component * @param noiseNormalizationSignaCbCr Noise normalization sigma for Cb, Cr components * @param doubleFHT - DoubleFHT instance or null (reusing the same instance will use cache for sin/cos/window arrays) * @param binaryAlpha Consider all points with alpha>0 to have same alpha==1.0 * @param minTileFraction Minimal tile overlap fraction to process tile (will be squared in the corners of the tile map) * @param threadsMax Maximal number of concurrent threads to run * @param debugLevel Debug level * @return accumulated Correlation results for the specified range of disparities as [0][], [1][] = number of samples averaged */ public double [][] correlateMultipleShifts( int side, int self, int size, int corrXC, int corrYC, int [] shifts, // >=0; double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, boolean binaryAlpha, double minTileFraction, // decrease for corners DoubleFHT doubleFHT, int threadsMax, int debugLevel){ if (debugLevel>2){ String dbgStr="correlateMultipleShifts("+side+","+size+","+ corrXC+","+corrYC+",{"; for (int nShft=0; nShft0)?", ":"")+shifts[nShft]; } dbgStr+="},...)"; System.out.println(dbgStr); } int corrFFTSize=2*size; int numLayers=this.overlapImages.length/2; int length=corrFFTSize*corrFFTSize; if (doubleFHT==null) doubleFHT = new DoubleFHT(); double[] hamming1d=doubleFHT.getHamming1d(corrFFTSize); double [] componentWeights={ 1.0/(1.0+corrCbWeight+corrCrWeight), corrCbWeight/(1.0+corrCbWeight+corrCrWeight), corrCrWeight/(1.0+corrCbWeight+corrCrWeight)}; double [][] accumulatedCorrelation=new double[2][this.maxDisparity-this.minDisparity+1]; for (int n=0;n0)?-1:1; int selectionType=(self!=0)?(side+1):0; // 0 - normal correlation, 1 - left auto, 2 - right auto for (int nShift=0;nShift2){ System.out.println("correlateMultipleShifts(): Selection ("+(binaryAlpha?"binary":"original")+" alpha) xc="+corrXC+" yc="+corrYC+" corrFFTSize="+corrFFTSize+" shift="+shifts[nShift]+ " : first "+IJ.d2s(100*stats[0],3)+"%, second "+IJ.d2s(100*stats[1],3)+"%, overlap "+IJ.d2s(100*stats[2],3)+"%"); } if (stats[2]2){ System.out.println("Too little overlap - "+IJ.d2s(100*stats[2],3)+"%, abandoning tile XC="+corrXC+" YC="+corrXC); } continue; } double [][] window={selection[0].clone(),selection[numLayers].clone()}; for (int iy=0;iyaccumulatedCorrelation[0].length) resultNumber=accumulatedCorrelation[0].length-resultIndex; if (resultNumber<=0){ if (debugLevel>1){ System.out.println("Correlation results do not intersect with the accumulation array"); } continue; } if (debugLevel>2){ System.out.println("resultNumber="+resultNumber+" resultIndex="+resultIndex+" index0="+index0); } double [][] corr=new double[numLayers-1][length]; for (int i=0;i0){ double rms=hFNoiseNormalize( corr[i], // double [] data, sigma, // double filterSigma, true); // boolean centerOnly); if (this.debugLevel>2){ System.out.println("Correlation RMS for component "+((i==0)?"Y":((i==1)?"Cb":"Cr"))+ " was "+rms); } } // may only calculate for the middle line for (int j=0;j0){ accumulatedCorrelation[0][i]/=accumulatedCorrelation[1][i]; numPoints++; } } if (numPoints==0){ if (debugLevel>1){ System.out.println("Nothing accumulated"); } return null; } return accumulatedCorrelation; } // old version (*0), delete public double [][] collectDisparityFromTiles0( int debugLevel){ double [][] disparity=new double [6][this.mapWidth*this.mapHeight]; for (int n=0;nthis.mapHeight) yMaxLim=this.mapHeight; if (xMaxLim>this.mapWidth) xMaxLim=this.mapWidth; if (debugLevel>1) System.out.println("collectDisparityFromTiles(): this.tilesSize="+this.tilesSize+",yMinLim="+yMinLim+ ", yMaxLim="+yMaxLim+", xMinLim="+xMinLim+", xMaxLim="+xMaxLim); for (int iY=yMinLim;iY=0;level--){ int size = (this.tilesSize>>level); double [][] tile=this.tiles0[level][tY/size][tX/size]; if (tile!=null){ int index=iY*this.mapWidth+iX; disparity[2][index]=tile[0][0]; disparity[3][index]=tile[0][1]; // contrast background disparity[4][index]=tile[tile.length-1][0]; // last - FG if available, if not - BG disparity[5][index]=tile[tile.length-1][1]; // contrast *-ground disparity[0][index]=(disparity[3][index]>disparity[5][index])?disparity[2][index]:disparity[4][index]; disparity[1][index]=(disparity[3][index]>disparity[5][index])?disparity[3][index]:disparity[5][index]; break; } } } } return disparity; } public int [] generateTileLevels0( double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double contrastThreshold, double contrastThresholdDecrease, //Decrease threshold for smaller tiles as 1/2^(k*level), k= boolean binaryAlpha, boolean enableNegativeDisparity, double minTileFraction, DoubleFHT doubleFHT, int threadsMax, int debugLevel){ if (doubleFHT==null) doubleFHT=new DoubleFHT(); int numNonEmptyTiles=0; int numWithForeground=0; for (int level=0;level1){ System.out.println("On level "+level+": non-empty tiles="+tileResult[0]+" tiles with foreground="+tileResult[1]); } numNonEmptyTiles+= tileResult[0]; numWithForeground+=tileResult[1]; } int [] result={numNonEmptyTiles,numWithForeground}; return result; } // Convert to multithread public int [] generateTileLevel0( int level, double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double contrastThreshold, boolean binaryAlpha, boolean enableNegativeDisparity, double minTileFraction, DoubleFHT doubleFHT, int threadsMax, int debugLevel){ int numNonEmptyTiles=0; int numWithForeground=0; for (int tileY=0;tileY1){ numWithForeground++; } } } int [] result={numNonEmptyTiles,numWithForeground}; return result; } /** * Generate disparity tile by correlating the images, starting with info from the higher level tile (if available) * @param level level of the tile to generate * @param tileX Horizontal tile number * @param tileY Vertical tile number * @param phaseCorrelationFraction 1.0 - use pure phase correlation, 0.0 - just correlation * @param correlationHighPassSigma Correlation high-pass filter sigma, pixels in frequency domain * @param correlationLowPassSigma Correlation low-pass filter sigma, fraction of the frequency range * @param noiseNormalizationSignaY Noise normalization sigma for Y-component * @param noiseNormalizationSignaCbCr Noise normalization sigma for Cb, Cr components * @param contrastThreshold Use correlation data with normalized values above this threshold only * @param binaryAlpha consider all points with alpha>0 to have same alpha==1.0 * @param minTileFraction minimal tile overlap fraction to process tile (will be squared in the corners of the tile map) * @param threadsMax Maximal number of concurrent threads to run * @param debugLevel Debug level * @return Generated tile (or null if no tile was generated) */ public double [][] generateTile0( int level, int tileX, int tileY, double phaseCorrelationFraction, double corrCbWeight, double corrCrWeight, double correlationHighPassSigma, double correlationLowPassSigma, double noiseNormalizationSignaY, double noiseNormalizationSignaCbCr, double contrastThreshold, boolean binaryAlpha, boolean enableNegativeDisparity, double minTileFraction, DoubleFHT doubleFHT, int threadsMax, int debugLevel){ int tileDataLength=5; // only 2 used? int size=(this.tilesSize>>level); int corrFFTSize=2*size; int numLayers=this.overlapImages.length/2; boolean isCorner=( ((tileY==0)||(tileY==(this.tiles0[level].length))) && ((tileX==0)||(tileX==(this.tiles0[level][0].length)))); if (isCorner) minTileFraction*=minTileFraction; int corrXC=this.tilesX0+tileX*size+size/2; int corrYC=this.tilesY0+tileY*size+size/2; double [][] parentTile; if ( level==0){ parentTile=new double[1][]; // {position,strength,rmsY,rmsCb,rmsCr} parentTile[0]=new double [tileDataLength]; parentTile[0][0]=0; // center position, all the rest are not needed } else { parentTile= this.tiles0[level-1][tileY/2][tileX/2]; } if (parentTile==null) return null; // no parent to this tile double [][] corrResults ={null,null}; int [] shifts={-1,-1}; // undefined double[] hamming1d=doubleFHT.getHamming1d(corrFFTSize); double [] componentWeights={ 1.0/(1.0+corrCbWeight+corrCrWeight), corrCbWeight/(1.0+corrCbWeight+corrCrWeight), corrCrWeight/(1.0+corrCbWeight+corrCrWeight)}; int length=corrFFTSize*corrFFTSize; for (int numBgFg=0;numBgFg1){ System.out.println("Selection ("+(binaryAlpha?"binary":"original")+" alpha) xc="+corrXC+" yc="+corrYC+" corrFFTSize="+corrFFTSize+" shift="+shifts[numBgFg]+ " : first "+IJ.d2s(100*stats[0],3)+"%, second "+IJ.d2s(100*stats[1],3)+"%, overlap "+IJ.d2s(100*stats[2],3)+"%"); } if (stats[2]1){ System.out.println("Too little overlap - "+IJ.d2s(100*stats[2],3)+"%, abandoning tile XC="+corrXC+" YC="+corrXC); } continue; } if (doubleFHT==null) doubleFHT = new DoubleFHT(); double [][] window={selection[0].clone(),selection[numLayers].clone()}; for (int iy=0;iy0){ double rms=hFNoiseNormalize( corr[i+1], // double [] data, sigma, // double filterSigma, true); // boolean centerOnly); if (this.debugLevel>2){ System.out.println("Correlation RMS for component "+((i==0)?"Y":((i==1)?"Cb":"Cr"))+ " was "+rms); } } for (int j=0;j1){ String dbgStr="corrResults["+numBgFg+"]"; if (corrResults[numBgFg]==null) dbgStr+="=null"; else { dbgStr+=": min="+IJ.d2s(corrResults[numBgFg][0],3)+ ", max="+IJ.d2s(corrResults[numBgFg][1],3)+ ", MAX1="+IJ.d2s(corrResults[numBgFg][2],3)+" ("+IJ.d2s(corrResults[numBgFg][3],3)+")"; for (int j=2;j<(corrResults[numBgFg].length/2);j++){ dbgStr+=", MAX"+j+"="+IJ.d2s(corrResults[numBgFg][2*j],3)+" ("+IJ.d2s(corrResults[numBgFg][2*j+1],3)+")"; } } System.out.println(dbgStr); } } if ((corrResults[0]==null) && (corrResults[1]==null)) return null; // both are nulls // combine results from both FG and BG int minShift=this.tilesSize,maxShift=0; for (int numBgFg=0;numBgFg<2;numBgFg++) if (corrResults[numBgFg]!=null){ for (int nMax=0;(nMax<2) && (nMax<(corrResults[numBgFg].length/2-1));nMax++){ int shift=(int) Math.round(corrResults[numBgFg][nMax*2+2]); if (shiftmaxShift) maxShift=shift; } } int [] oldShifts=shifts.clone(); // element = -1 - never tried shifts=new int [(maxShift>minShift)?2:1]; shifts[0]=minShift; shifts[shifts.length-1]=maxShift; // OK if minShift==maxShift // use corrResults if the shifts are the same double [][] oldResults=corrResults.clone(); corrResults=new double [shifts.length][]; boolean [] newShift={false,false}; for (int i=0;i1){ System.out.println("Selection-second pass ("+(binaryAlpha?"binary":"original")+" alpha) xc="+corrXC+" yc="+corrYC+" corrFFTSize="+corrFFTSize+" shift="+shifts[numShift]+ " : first "+IJ.d2s(100*stats[0],3)+"%, second "+IJ.d2s(100*stats[1],3)+"%, overlap "+IJ.d2s(100*stats[2],3)+"%"); } if (stats[2]1){ System.out.println("Too little overlap - "+IJ.d2s(100*stats[2],3)+"%, abandoning tile XC="+corrXC+" YC="+corrXC); } continue; } double [][] window={selection[0].clone(),selection[numLayers].clone()}; for (int iy=0;iy0){ double rms=hFNoiseNormalize( corr[i+1], // double [] data, sigma, // double filterSigma, true); // boolean centerOnly); if (this.debugLevel>2){ System.out.println("--Correlation RMS for component "+((i==0)?"Y":((i==1)?"Cb":"Cr"))+ " was "+rms); } } // for (int j=0;j1){ String dbgStr="corrResults["+numShift+"]"; if (corrResults[numShift]==null) dbgStr+="=null"; else { dbgStr+=": min="+IJ.d2s(corrResults[numShift][0],3)+ ", max="+IJ.d2s(corrResults[numShift][1],3)+ ", MAX1="+IJ.d2s(corrResults[numShift][2],3)+" ("+IJ.d2s(corrResults[numShift][3],3)+")"; for (int j=2;j<(corrResults[numShift].length/2);j++){ dbgStr+=", MAX"+j+"="+IJ.d2s(corrResults[numShift][2*j],3)+" ("+IJ.d2s(corrResults[numShift][2*j+1],3)+")"; } } System.out.println(dbgStr); } } // now one or 2 correlations were run with shifts[] , each of the results is expected to be close to those (that) double [][] rslt = {null,null}; if ((corrResults[0]==null) && ((corrResults.length==1) || (corrResults[1]==null))) return null; if ((corrResults.length>1) && (corrResults[0]!=null) && (corrResults[1]!=null)){ // two results for (int i=0;i4){ rslt[1]=new double[2]; rslt[1][0]=corrResults[i][4]; rslt[1][1]=corrResults[i][5]; } } // reorder to have background first if ((rslt[1]!=null) && (rslt[1][0]0) && (this.overlapImages[numLayers][index]>0)){ if (iy>maxY) maxY=iy; if (iymaxX) maxX=ix; if (ix0) System.out.println("No overlap"); return null; } int width= (maxX-minX+1); int height=(maxY-minY+1); this.tilesNHor=width/size; if ((this.tilesNHor*size)0)?shiftX:0)}; int y0=yc-height/2; int length=width*height; int numSensors=this.channel.length; // double [][] selection = new double[this.overlapImages.length][length]; double [][] selection = new double[this.overlapImages.length][]; for (int i=0;i0)?shiftX:0); break; default:x0[i]=xc-width/2; } } if ((numSensors!=2) || (this.overlapImages[0]==null) || (this.overlapImages[0]==null)) { pairType=0; } for (int iy=0;iy=this.mapHeight); for (int ix=0;ix0){ modNumImg=(pairType==1)?0:1; } int oIndex=iy*width+ix; int srcX=x0[numImg]+ix; // int numNonZero=0; if (oob ||(srcX<0) || (srcX>=this.mapWidth)) { for (int i=0;i0)){ // System.out.print(iy+":"+ix+" i="+i+" i+modNumImg*numLayers="+(i+modNumImg*numLayers)+" "); // numNonZero++; // } } } // if (numNonZero>1) { // System.out.print(iy+":"+ix); // System.out.print("="+numNonZero+" "); // } } } } return selection; } /* public double [] getSelection( int width, int height, int numImg, int chn, int xc, int yc){ int length=width*height; int numSensors=this.channel.length; double [] selection = new double[length]; int numLayers=this.overlapImages.length/numSensors; int y0=yc-height/2; int x0=xc-width/2; for (int iy=0;iy=this.mapHeight); for (int ix=0;ix=this.mapWidth)) { selection[oIndex]=0.0; } else { selection[oIndex]=this.overlapImages[chn+numImg*numLayers][srcY*this.mapWidth+srcX]; } } } return selection; } */ public double [] getSelection( double [] imageSlice, // one image/channel slice double [] selection, // null or array to reuse int width, int height, int xc, int yc){ int length=width*height; if (selection ==null) selection = new double[length]; int y0=yc-height/2; int x0=xc-width/2; for (int iy=0;iy=this.mapHeight); for (int ix=0;ix=this.mapWidth)) { selection[oIndex]=0.0; } else { selection[oIndex]=imageSlice[srcY*this.mapWidth+srcX]; } } } return selection; } public double [][] getSelection( int width, int height, int [][] iCenterXY){ int length=width*height; int numSensors=this.channel.length; double [][] selection = new double[this.overlapImages.length][]; for (int i=0;i=this.mapHeight); for (int ix=0;ix=this.mapWidth)) { for (int i=0;i0.0)?1.0:0.0; a1=(a1>0.0)?1.0:0.0; } stats[0]+=a0; stats[1]+=a1; stats[2]+=a0*a1; } for (int i=0;i0){ // Prevent NaN if there is no data (i.e. oversaturated area) double rrms=1.0/rms; for (int i=0;i=this.channel.length){ String msg="This map is for channel"+((this.channel.length>1)?"s ":" "); for (int i=0;i0) msg +=","; msg+=" "+this.channel[i]; } msg+=", and this image is for channel "+channel; System.out.println(msg); IJ.showMessage(msg); return null; } // boolean second=(channel ==this.channel[1]); final float [][] map={this.map[index*3],this.map[index*3+1],this.map[index*3+2]}; final int [] imagePixels=(int []) imp.getProcessor().getPixels(); final int mapWidth= this.mapWidth; final int mapHeight=this.mapHeight; final int [] outPixels=new int [mapWidth*mapHeight]; final int center=lanczos[0][0][0].length/2; final Thread[] threads = newThreadArray(maxThreads); final AtomicInteger opyAtomic = new AtomicInteger(0); final AtomicInteger opyDoneAtomic = new AtomicInteger(1); // for (int opy=0;opy0){ // do not convolve pixels with alpha=0; double x=scale*map[0][oIndex]; double y=scale*map[1][oIndex]; int ix= (int) Math.round(x); int iy= (int) Math.round(y); double dx=x-ix; double dy=y-iy; int indxX= (int) Math.round((2*dx+1)*binsPerHalfPixel); int indxY= (int) Math.round((2*dy+1)*binsPerHalfPixel); double [][] lk=lanczos[indxY][indxX]; for (int i=0;i=height) ipy=height-1; int baseI=ipy*width; for (int j=0;j=width) ipx=width-1; int pix=imagePixels[baseI+ipx]; RGBA[1]+=((pix>>16) & 0xff)*lk[i][j]; // R RGBA[2]+=((pix>>8) & 0xff)*lk[i][j]; // G RGBA[3]+=( pix & 0xff)*lk[i][j]; // B } } int c=0; for (int i=0;i<4;i++){ if (RGBA[i]< 0.0) RGBA[i]=0.0; else if (RGBA[i] > 255.0) RGBA[i]=255.0; c=(c<<8) | ((int) RGBA[i]); outPixels [oIndex]=c; } } else { outPixels[oIndex]=0; } } final int numFinished=opyDoneAtomic.getAndIncrement(); // IJ.showProgress(progressValues[numFinished]); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // Here, we can safely update the GUI // because we'll be called from the // event dispatch thread IJ.showProgress(numFinished,mapHeight); } }); } } }; } startAndJoin(threads); ColorProcessor cp=new ColorProcessor(mapWidth,mapHeight); cp.setPixels(outPixels); String imageTitle; if (isOverlap && (this.channel.length>=2)) imageTitle=imp.getTitle()+String.format("OVLP_%02d-%02d",this.channel[0],this.channel[1]); else imageTitle=imp.getTitle()+"_PLNPRJ"; //isOverlap ImagePlus impOut=new ImagePlus(imageTitle,cp); impOut.setProperty("channel", ""+channel); // image channel for (int i=0;i=sensorCorrWidth) ix[i]=sensorCorrWidth-1; if (iy[i]<0) iy[i]=0; else if (iy[i]>=sensorCorrHeight) iy[i]=sensorCorrHeight-1; } int index00=ix[0] + iy[0]*sensorCorrWidth; int indexX0=ix[1] + iy[0]*sensorCorrWidth; int index0Y=ix[0] + iy[1]*sensorCorrWidth; int indexXY=ix[1] + iy[1]*sensorCorrWidth; double corrDX=0,corrDY=0; if ((px>ix[0])&& (pxiy[0])&& (py=v.length){ System.out.println("getBayerFlatField(): v.length="+v.length+" x="+x+" y="+y+" iBayer="+iBayer); } double d=v[iBayer]; if (d!=0.0) d=1.0/d; corrScale[y*width+x]=d; } return corrScale; } public float [] getBayerFlatFieldFloat( int [][] bayer, //{{1,0},{2,1}} GR/BG double range) { return _getBayerFlatFieldFloat( this.pixelCorrectionWidth, // width, this.pixelCorrectionHeight, // int height, bayer, range); } private float [] _getBayerFlatFieldFloat( int width, int height, int [][] bayer, //{{1,0},{2,1}} GR/BG double range) { float [] corrScale = new float [width*height]; for (int y=0;y=v.length){ System.out.println("getBayerFlatField(): v.length="+v.length+" x="+x+" y="+y+" iBayer="+iBayer); } double d=v[iBayer]; if (d!=0.0) d=1.0/d; corrScale[y*width+x]= (float) d; } if (range > 0.0) { double [] mins = {Double.NaN,Double.NaN,Double.NaN,Double.NaN}; int indx = 0; for (int y=0;y= mins[iBayer])) mins[iBayer] = d; } for (int i = 0; i < mins.length; i++) { mins[i] *= range; } indx = 0; for (int y=0;y= mins[iBayer]) corrScale[indx] = (float) mins[iBayer]; indx++; } } return corrScale; } public int [][] getDefectsXY(){ return this.defectsXY; } public double [] getDefectsDiff(){ return this.defectsDiff; } public class DirectMap{ public int width; public int height; public double x0; public double y0; public double pixelStep; public double [][] map; public DirectMap( int width, int height, double x0, double y0, double pixelStep, boolean flat, int numLayers){ this.width=width; this.height=height; this.x0=x0; this.y0=y0; this.pixelStep=pixelStep; this.map=new double[numLayers][width*height]; } } public class EquirectangularMap{ public int channel= -1; public double longitudeLeft= -180.0; public double longitudeRight= 180.0; public double latitudeTop= 90.0; public double latitudeBottom= -90.0; public int pixelsHorizontal=14000; public int pixelsVertical; public double degreesPerPixel; public int width; public int height; public double x0; public double y0; public double pixelStep; public double minAlpha=0.1; // ignore pixels with mask below this value public int numLayers; public float [][] map; // saving memory : first index - pixel #, empty - null, otherwise {x,y,alpha, ...) public float [][] partialMap; // public Rectangle mapWOI=null; // selection of the partialMap in the overall map. May public EquirectangularMap(String path){ setMapFromImageStack(path); this.degreesPerPixel=(this.longitudeRight-this.longitudeLeft)/(this.pixelsHorizontal-0); // with 360 - last pixel (this.longitudeRight) IS NOT included ! } // public boolean flat=true; public EquirectangularMap( int channel, double longitudeLeft, double longitudeRight, double latitudeTop, double latitudeBottom, int pixelsHorizontal, int width, int height, double x0, double y0, double pixelStep, int numLayers){ this.channel= channel; this.longitudeLeft= longitudeLeft; this.longitudeRight= longitudeRight; this.latitudeTop= latitudeTop; this.latitudeBottom= latitudeBottom; this.pixelsHorizontal=pixelsHorizontal; while (this.longitudeRightthis.latitudeTop){ double tmp=this.latitudeBottom; this.latitudeBottom=this.latitudeTop; this.latitudeTop=tmp; } this.degreesPerPixel=(this.longitudeRight-this.longitudeLeft)/(this.pixelsHorizontal-0); // with 360 - last pixel (this.longitudeRight) IS NOT included ! this.pixelsVertical=(int) Math.round((this.latitudeTop-this.latitudeBottom)/this.degreesPerPixel) +1; // last pixel (this.latitudeBottom) IS included this.width=width; this.height=height; this.x0=x0; this.y0=y0; this.pixelStep=pixelStep; this.numLayers=numLayers; this.map=new float[this.pixelsVertical*this.pixelsHorizontal][]; } public ImagePlus saveMapAsImageStack(String title, String path){ ImagePlus imp=getMapAsImageStack(title); if (imp==null) return null; FileSaver fs=new FileSaver(imp); if (imp.getStackSize()>1) fs.saveAsTiffStack(path); else fs.saveAsTiff(path); return imp; } public ImagePlus getMapAsImageStack(String title){ if (this.partialMap==null){ String msg="Partial equirectangular map does not exist"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } ImageStack stack=new ImageStack(this.mapWOI.width,this.mapWOI.height); for (int n=0;niLat) minILat=iLat; if (maxILat=0 } this.mapWOI=new Rectangle(minILong,minILat,width,maxILat-minILat+1); this.partialMap=new float [numLayers][this.mapWOI.width*this.mapWOI.height]; int indexDst=0; int numPix=0; for (int iLat=0;iLat2) IJ.showMessage("Error",msg); if (this.debugLevel>0)System.out.println(msg); return; } if (this.debugLevel>1) System.out.println("Read "+path+" as a sensor calibration data"); (new JP46_Reader_camera(false)).decodeProperiesFromInfo(imp); setSensorDataFromImageStack(imp); } public SensorData (){} // just to get parameter names public void setChannel(int chn){this.channel = chn;} public int getChannel(){return this.channel;} public int getSubChannel(){return this.subchannel;} public int getSubCamera(){return this.subcamera;} public int getSensorPort(){return this.sensor_port;} public void setSensorDataFromImageStack(ImagePlus imp){ // int corrX=0,corrY=1,corrMask=2; boolean need_lineTime_fix = false; // will use sensor default if not provided, 3.638E-5 for RGB and 2.7778e-05 for Boson boolean need_monochrome_fix = false; // will use sensor default if not provided, 3.638E-5 for RGB and 2.7778e-05 for Boson boolean need_lwir_fix = false; // will use sensor default if not provided, 3.638E-5 for RGB and 2.7778e-05 for Boson if (imp == null){ String msg="Sensor Calibration image is null"; IJ.showMessage("Error",msg); throw new IllegalArgumentException (msg); } String [] requiredProperties={ "pixelCorrectionWidth", "pixelCorrectionHeight", "pixelCorrectionDecimation", "distortionRadius", "focalLength", "pixelSize", "distortionA5", "distortionA", "distortionB", "distortionC", "px0", "py0", "azimuth", "radius", "height", "heading", "elevation", "roll", "channel", "lineTime", // optional "monochrome", // optional "lwir" // optional }; for (int i=0; i=10) && (iy<=20) && (ix>=10) && (ix<=20); int outIndex=ix+this.directMap.width*iy; double x=x0+pixelStep*ix; double y=y0+pixelStep*iy; int corrX=((int) Math.floor(x/this.pixelCorrectionDecimation)); int corrY=((int) Math.floor(y/this.pixelCorrectionDecimation)); double corrDX=x/this.pixelCorrectionDecimation-corrX; double corrDY=y/this.pixelCorrectionDecimation-corrY; if (corrX<0) {corrX=0;corrDX=0;} else if (corrX>=sensorCorrWidth) {corrX=sensorCorrWidth-1;corrDX=0;} if (corrY<0) {corrY=0;corrDY=0;} else if (corrY>=sensorCorrHeight) {corrY=sensorCorrHeight-1;corrDY=0;} int index00=corrX + corrY*sensorCorrWidth; int indexX0=index00+((corrX==(sensorCorrWidth-1))?0:1); int index0Y=index00+((corrY==(sensorCorrHeight-1))?0:sensorCorrWidth); int indexXY=index0Y+((corrX==(sensorCorrWidth-1))?0:1); if (debugThis){ System.out.println("combineDistortionsSensorToFlat(): x="+x+" y="+y+" corrX="+corrX+ " corrY="+corrY+" corrDX="+corrDX+ " corrDY="+corrDY); System.out.println("combineDistortionsSensorToFlat(): index00="+index00+" indexX0="+indexX0+" index0Y="+index0Y+" indexXY="+indexXY); } // Linear interpolate all this.pixelCorrection[i][indexXY] double [] corr=new double [this.pixelCorrection.length]; for (int n=0;n0){ // potentially visible v[0]*=focalLength/v[2]; v[1]*=focalLength/v[2]; v[2]= focalLength; double rND=Math.sqrt(v[0]*v[0]+v[1]*v[1]); double r=rND/distortionRadius; if (r2) && ((r_xyod[i][0]!=0.0) || (r_xyod[i][0]!=0.0))){ // System.out.println ("i="+i+" r_xyod[i][0]="+r_xyod[i][0]+" r_xyod[i][1]="+r_xyod[i][1]+" a[0]="+a[0]+" a[1]="+a[1]+" a[2]="+a[2]+" a[3]="+a[3]); // } } // double [] xyDist={v[0]+distortionRadius*deltaX, v[1]+distortionRadius*deltaY}; pXY=new double[2]; // convert to sensor pixels coordinates pXY[0]= 1000.0/pixelSize*(v[0]+distortionRadius*deltaX) + px0; pXY[1]=-1000.0/pixelSize*(v[1]+distortionRadius*deltaY) + py0; /* double k; if (use8) k=((((((distortionA8*r+distortionA7)*r+distortionA6)*r+distortionA5)*r + distortionA)*r+distortionB)*r+distortionC)*r+d; else k=(((distortionA5*r + distortionA)*r+distortionB)*r+distortionC)*r+d; // calculate in sensor pixel pXY[0]= v[0]*k*1000.0/pixelSize+px0; // in pixels, right pXY[1]=-v[1]*k*1000.0/pixelSize+py0; // in pixels, down */ // un-apply residual correction, for now - consider it small/smooth to skip reverse mapping, make it a two step int corrX=((int) Math.floor(pXY[0]/pixelCorrectionDecimation)); int corrY=((int) Math.floor(pXY[1]/pixelCorrectionDecimation)); if (corrX<0) corrX=0; else if (corrX>=sensorCorrWidth) corrX=sensorCorrWidth-1; if (corrY<0) corrY=0; else if (corrY>=sensorCorrHeight) corrY=sensorCorrHeight-1; // x1, y1 - one stage correction double x1=pXY[0]+pixelCorrection[0][corrX + corrY*sensorCorrWidth]; // positive correction double y1=pXY[1]+pixelCorrection[1][corrX + corrY*sensorCorrWidth]; // second step correction corrX=((int) Math.floor(x1/pixelCorrectionDecimation)); corrY=((int) Math.floor(y1/pixelCorrectionDecimation)); double corrDX=x1/pixelCorrectionDecimation-corrX; double corrDY=y1/pixelCorrectionDecimation-corrY; if (corrX<0) {corrX=0;corrDX=0;} else if (corrX>=sensorCorrWidth) {corrX=sensorCorrWidth-1;corrDX=0;} if (corrY<0) {corrY=0;corrDY=0;} else if (corrY>=sensorCorrHeight) {corrY=sensorCorrHeight-1;corrDY=0;} int index00=corrX + corrY*sensorCorrWidth; int indexX0=index00+((corrX==(sensorCorrWidth-1))?0:1); int index0Y=index00+((corrY==(sensorCorrHeight-1))?0:sensorCorrWidth); int indexXY=index0Y+((corrX==(sensorCorrWidth-1))?0:1); // Linear interpolate all this.pixelCorrection[i][indexXY] double [] corr=new double [pixelCorrection.length]; for (int n=0;n0){ // potentially visible v[0]*=focalLength/v[2]; v[1]*=focalLength/v[2]; v[2]= focalLength; double rND=Math.sqrt(v[0]*v[0]+v[1]*v[1]); double r=rND/distortionRadius; if (r=sensorCorrWidth) corrX=sensorCorrWidth-1; if (corrY<0) corrY=0; else if (corrY>=sensorCorrHeight) corrY=sensorCorrHeight-1; // x1, y1 - one stage correction double x1=pXY[0]+pixelCorrection[0][corrX + corrY*sensorCorrWidth]; // positive correction double y1=pXY[1]+pixelCorrection[1][corrX + corrY*sensorCorrWidth]; // second step correction corrX=((int) Math.floor(x1/pixelCorrectionDecimation)); corrY=((int) Math.floor(y1/pixelCorrectionDecimation)); double corrDX=x1/pixelCorrectionDecimation-corrX; double corrDY=y1/pixelCorrectionDecimation-corrY; if (corrX<0) {corrX=0;corrDX=0;} else if (corrX>=sensorCorrWidth) {corrX=sensorCorrWidth-1;corrDX=0;} if (corrY<0) {corrY=0;corrDY=0;} else if (corrY>=sensorCorrHeight) {corrY=sensorCorrHeight-1;corrDY=0;} int index00=corrX + corrY*sensorCorrWidth; int indexX0=index00+((corrX==(sensorCorrWidth-1))?0:1); int index0Y=index00+((corrY==(sensorCorrHeight-1))?0:sensorCorrWidth); int indexXY=index0Y+((corrX==(sensorCorrWidth-1))?0:1); // Linear interpolate all this.pixelCorrection[i][indexXY] double [] corr=new double [pixelCorrection.length]; for (int n=0;n=(this.rByRDist.length-1)) { if (debug) System.out.println("getRByRDist("+IJ.d2s(rDist,3)+"): index="+index+">="+(this.rByRDist.length-1)); return Double.NaN; } double result=this.rByRDist[index]+(this.rByRDist[index+1]-this.rByRDist[index])*(rDist/this.stepR-index); if (Double.isNaN(result)){ if (debug) System.out.println("this.rByRDist["+index+"]="+this.rByRDist[index]); if (debug) System.out.println("this.rByRDist["+(index+1)+"]="+this.rByRDist[index+1]); if (debug) System.out.println("rDist="+rDist); if (debug) System.out.println("(rDist/this.stepR="+(rDist/this.stepR)); } return result; } } /* Create a Thread[] array as large as the number of processors available. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ private Thread[] newThreadArray(int maxCPUs) { int n_cpus = Runtime.getRuntime().availableProcessors(); if (n_cpus>maxCPUs)n_cpus=maxCPUs; return new Thread[n_cpus]; } /* Start all given threads and wait on each of them until all are done. * From Stephan Preibisch's Multithreading.java class. See: * http://repo.or.cz/w/trakem2.git?a=blob;f=mpi/fruitfly/general/MultiThreading.java;hb=HEAD */ private static void startAndJoin(Thread[] threads) { for (int ithread = 0; ithread < threads.length; ++ithread) { threads[ithread].setPriority(Thread.NORM_PRIORITY); threads[ithread].start(); } try { for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread].join(); } catch (InterruptedException ie) { throw new RuntimeException(ie); } } }