/*
* PivEvaluation.java
*
* Copyright 2008 Peter Vennemann
*
* This file is part of JPIV.
*
* JPIV 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.
*
* JPIV 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 JPIV. If not, see <http://www.gnu.org/licenses/>.
*/
package jpiv2;
import javax.media.jai.PlanarImage;
import javax.media.jai.JAI;
import java.awt.image.renderable.ParameterBlock;
/**
* PIV Evaluation.
*
*/
public class PivEvaluation extends Thread {
private jpiv2.JPiv jpiv;
private jpiv2.Settings settings;
// correlation fields
private PlanarImage[] corr;
// A BufferedImage may be used instead of a PlanarImage to force the JAI
// operation chain to be executed. When using a PlanarImage, all operations
// are stored in memory until the data is requested (jai pull model). This
// leads to higher computation speed, but also to higher memory usage.
private int passes;
private int p = 0;
private int[] intWinW;
private int[] intWinH;
private int[] searchAreaW;
private int[] searchAreaH;
private int[] horVecSpacing;
private int[] verVecSpacing;
private int horPreShift;
private int verPreShift;
// initial vector field
private int x0, y0, x1, y1, nx, ny;
private double[][] shift;
// first derivative for window deformation
private double[][][] deriv;
private int[] dim;
// file handling
private String[] files;
private String destFileName;
private String lastDestFileName = null;
private java.text.DecimalFormat fileNumFormat;
/**
* Creates a new instance of PivEvaluation
*
* @param jpiv
* The jpiv2.JPiv mother component.
*/
public PivEvaluation(jpiv2.JPiv jpiv) {
this.jpiv = jpiv;
}
/**
* Overrides run() in java.lang.Thread. Never call run() directly. Use
* jpiv2.PivEvaluation.start(), inherited from java.lang.Thread to start an
* instance of PivEvaluation as a new thread. When the PIV evaluation is
* finished, jpiv.getListFrame().notify() is called. Thus, you can use
* jpiv.getListFrame().wait() to stop your thread as long as this thread is
* busy. In this way, you can automatically start a couple of PivEvaluation
* threads and modify some parameters in between.
*/
@Override
public void run() {
jpiv2.ListFrame lf = jpiv.getListFrame();
synchronized (lf) {
files = lf.getSelectedElements();
if (files == null) {
System.out.println("No files selected.");
} else {
doPivEvaluation();
lf.notify();
}
}
}
/**
* Does the actual PIV evaluation. This function does the looping over the
* passes and the images and writes the output into files. If you want to
* run the PIV evaluation in a separate thread, call the method start()
* instead.
*
* @return The last used filename.
*/
public String doPivEvaluation() {
initVariables();
lastDestFileName = null;
if (settings.pivUseImageBaseName || settings.pivUseDefaultDestFileName
|| chooseDestFileName()) {
System.out.println("JPiv PIV evaluation started.");
jpiv2.PivData theData;
jpiv2.PivImg pivImg;
for (p = 0; p < passes; p++) {
for (int f = 0; f < files.length; f++) {
switch (settings.pivSequence) {
case jpiv2.Settings.PIV_CONSECUTIVE: {
if (f == 0)
f = 1;
pivImg = new PivImg(jpiv, files[f - 1], files[f]);
System.out.println("Processing: "
+ jpiv2.FileHandling
.stripFilename(files[f - 1]) + " and "
+ jpiv2.FileHandling.stripFilename(files[f]));
break;
}
case jpiv2.Settings.PIV_SKIP: {
int skip = settings.pivSkip;
if (f == 0)
f = skip + 1;
pivImg = new PivImg(jpiv, files[f - skip - 1], files[f]);
System.out.println("Processing: "
+ jpiv2.FileHandling.stripFilename(files[f
- skip - 1]) + " and "
+ jpiv2.FileHandling.stripFilename(files[f]));
break;
}
case jpiv2.Settings.PIV_CASCADE: {
if (f == 0)
f = 1;
pivImg = new PivImg(jpiv, files[0], files[f]);
System.out.println("Processing: "
+ jpiv2.FileHandling.stripFilename(files[0])
+ " and "
+ jpiv2.FileHandling.stripFilename(files[f]));
break;
}
case jpiv2.Settings.PIV_PAIRS: {
f++;
pivImg = new PivImg(jpiv, files[f - 1], files[f]);
System.out.println("Processing: "
+ jpiv2.FileHandling
.stripFilename(files[f - 1]) + " and "
+ jpiv2.FileHandling.stripFilename(files[f]));
break;
}
default: {
pivImg = new PivImg(jpiv, files[f]);
System.out.println("Processing: "
+ jpiv2.FileHandling.stripFilename(files[f]));
}
}
addCorrelation(pivImg);
if (!settings.exportCorrFunctionOnlySumOfCorr
|| f == files.length - 1) {
checkExportCorrFunction(f);
}
if (!settings.pivSumOfCorr) {
theData = calcDisplacements();
p++;
while (p < passes) {
initCorrMap();
System.out.println((p + 1) + " pass");
theData = doPostprocessing(theData);
theData.resample(x0, y0, x1, y1, horVecSpacing[p],
verVecSpacing[p]);
shift = theData.getPivDataShift();
if (settings.pivShearIntWindows)
deriv = theData.getShearField();
dim = theData.getDimension();
addCorrelation(pivImg);
if (!settings.exportCorrFunctionOnlySumOfCorr
|| f == files.length - 1) {
checkExportCorrFunction(f);
}
theData = calcDisplacements();
p++;
}
lastDestFileName = destFileName
+ fileNumFormat.format(f) + ".jvc";
theData.writeDataToFile(lastDestFileName,
settings.loadSaveTecplotHeader);
jpiv.getListFrame().appendElement(lastDestFileName);
// clean up for next evaluation
p = 0;
initCorrMap();
initPreShift();
}
}
if (!settings.pivSumOfCorr) {
System.out.println("JPiv PIV evaluation finished.");
return (lastDestFileName);
}
if (p < passes - 1) {
theData = calcDisplacements();
System.out.println((p + 2) + " pass");
theData = doPostprocessing(theData);
p++;
theData.resample(x0, y0, x1, y1, horVecSpacing[p],
verVecSpacing[p]);
shift = theData.getPivDataShift();
if (settings.pivShearIntWindows)
deriv = theData.getShearField();
dim = theData.getDimension();
initCorrMap();
p--;
}
}
p--;
theData = calcDisplacements();
lastDestFileName = destFileName + ".jvc";
theData.writeDataToFile(lastDestFileName,
settings.loadSaveTecplotHeader);
jpiv.getListFrame().appendElement(lastDestFileName);
System.out.println("PIV evaluation finished.");
}
return (lastDestFileName);
}
/**
* Return last used destination file name.
*
* @return Last used destination file name
*/
public String getLastUsedDestFileName() {
return (destFileName);
}
private void addCorrelation(jpiv2.PivImg pivImg) {
ParameterBlock pb = new ParameterBlock();
PlanarImage tmpCorr;
PlanarImage tmpCorrSum;
int x, y;
for (int c = 0; c < shift.length; c++) {
// without window deformation
if (!settings.pivShearIntWindows || p == 0) {
try {
// central difference
tmpCorr = PivUtil
.correlate(
pivImg.getSubImage(
(float) (shift[c][0] - (intWinW[p] + shift[c][2]) / 2),
(float) (shift[c][1] - (intWinH[p] + shift[c][3]) / 2),
intWinW[p], intWinH[p], 0),
pivImg.getSubImage(
(float) (shift[c][0] - (intWinW[p] - shift[c][2]) / 2),
(float) (shift[c][1] - (intWinH[p] - shift[c][3]) / 2),
intWinW[p], intWinH[p], 1));
} catch (java.lang.IllegalArgumentException tryBackwardDifference) {
try {
// backward difference
tmpCorr = PivUtil
.correlate(
pivImg.getSubImage(
(float) (shift[c][0]
- intWinW[p] / 2 - shift[c][2]),
(float) (shift[c][1]
- intWinH[p] / 2 - shift[c][3]),
intWinW[p], intWinH[p], 0),
pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p], 1));
} catch (java.lang.IllegalArgumentException tryForwardDifference) {
// forward difference
try {
tmpCorr = PivUtil.correlate(pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p], 0), pivImg
.getSubImage((float) (shift[c][0]
- intWinW[p] / 2 + shift[c][2]),
(float) (shift[c][1] - intWinH[p]
/ 2 + shift[c][3]),
intWinW[p], intWinH[p], 1));
} catch (java.lang.IllegalArgumentException fuckingHellNothingHelps) {
// no preshift, this might happen in the corners of
// an image, when
// the window size is not reduced from pass to pass.
tmpCorr = PivUtil
.correlate(
pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p], 0),
pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p], 1));
shift[c][2] = 0;
shift[c][3] = 0;
}
}
}
}
// with window deformation (experimental!!!)
else {
y = (int) (c / dim[0]);
x = c - y * dim[0];
try {
// central difference
tmpCorr = PivUtil
.correlate(
pivImg.getSubImage(
(float) (shift[c][0] - (intWinW[p] + shift[c][2]) / 2),
(float) (shift[c][1] - (intWinH[p] + shift[c][3]) / 2),
intWinW[p], intWinH[p],
(float) deriv[y][x][0] / 2,
(float) deriv[y][x][1] / 2, 0),
pivImg.getSubImage(
(float) (shift[c][0] - (intWinW[p] - shift[c][2]) / 2),
(float) (shift[c][1] - (intWinH[p] - shift[c][3]) / 2),
intWinW[p], intWinH[p],
(float) -deriv[y][x][0] / 2,
(float) -deriv[y][x][1] / 2, 1));
} catch (java.lang.IllegalArgumentException tryBackwardDifference) {
try {
// backward difference
tmpCorr = PivUtil
.correlate(
pivImg.getSubImage(
(float) (shift[c][0]
- intWinW[p] / 2 - shift[c][2]),
(float) (shift[c][1]
- intWinH[p] / 2 - shift[c][3]),
intWinW[p], intWinH[p],
(float) deriv[y][x][0] / 2,
(float) deriv[y][x][1] / 2, 0),
pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p],
(float) -deriv[y][x][0] / 2,
(float) -deriv[y][x][1] / 2, 1));
} catch (java.lang.IllegalArgumentException tryForwardDifference) {
// forward difference
try {
tmpCorr = PivUtil.correlate(pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p],
(float) deriv[y][x][0] / 2,
(float) deriv[y][x][1] / 2, 0), pivImg
.getSubImage((float) (shift[c][0]
- intWinW[p] / 2 + shift[c][2]),
(float) (shift[c][1] - intWinH[p]
/ 2 + shift[c][3]),
intWinW[p], intWinH[p],
(float) -deriv[y][x][0] / 2,
(float) -deriv[y][x][1] / 2, 1));
} catch (java.lang.IllegalArgumentException fuckingHellNothingHelps) {
// no preshift, this might happen in the corners of
// an image, when
// the window size is not reduced from pass to pass.
tmpCorr = PivUtil
.correlate(
pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p], intWinH[p],
(float) deriv[y][x][0] / 2,
(float) deriv[y][x][1] / 2,
0),
pivImg.getSubImage(
(float) (shift[c][0] - intWinW[p] / 2),
(float) (shift[c][1] - intWinH[p] / 2),
intWinW[p],
intWinH[p],
(float) -deriv[y][x][0] / 2,
(float) -deriv[y][x][1] / 2,
1));
shift[c][2] = 0;
shift[c][3] = 0;
}
}
}
}
// add correlation matrix
pb.removeParameters();
pb.removeSources();
pb.addSource(corr[c]);
pb.addSource(tmpCorr);
tmpCorrSum = JAI.create("add", pb, null);
corr[c] = tmpCorrSum;
}
}
private void checkExportCorrFunction(int file) {
// save sample function of one specific or all passes
if (settings.exportCorrFunction
&& settings.exportCorrFunctionNum != -1
&& (settings.exportCorrFunctionPass == p || settings.exportCorrFunctionPass == -1))
exportCorrFunction(corr[settings.exportCorrFunctionNum],
"_corrMap_file" + file + "_pass" + p + "_vector"
+ settings.exportCorrFunctionNum);
// save all functions of one specific or all passes
if (settings.exportCorrFunction
&& settings.exportCorrFunctionNum == -1
&& (settings.exportCorrFunctionPass == p || settings.exportCorrFunctionPass == -1))
for (int i = 0; i < corr.length; i++)
exportCorrFunction(corr[i], "_corrMap_file" + file + "_pass"
+ p + "_vector" + i);
}
private void exportCorrFunction(PlanarImage pi, String name) {
String baseName = FileHandling.stripExtension(destFileName);
ParameterBlock pb = new ParameterBlock();
pb.addSource(pi);
pb.add(baseName + name + "." + "tif");
pb.add("tiff");
JAI.create("filestore", pb);
}
private jpiv2.PivData doPostprocessing(jpiv2.PivData theData) {
if (settings.pivNormMedianTest) {
theData.normalizedMedianTest(settings.normMedianTestNoiseLevel,
settings.normMedianTestThreshold);
}
if (settings.pivReplace) {
theData.replaceByMedian(false, false);
}
if (settings.pivMedian) {
theData.replaceByMedian(true, true);
}
if (settings.pivSmoothing) {
theData.smooth(true);
}
return (theData);
}
private jpiv2.PivData calcDisplacements() {
double[] peak = new double[2];
for (int r = 0; r < shift.length; r++) {
if (settings.pivSumOfCorr) {
peak = PivUtil.getParabolicPeak(
PlanarImage.wrapRenderedImage(corr[r]), intWinW[p] / 2
- searchAreaW[p] / 2, intWinH[p] / 2
- searchAreaH[p] / 2, searchAreaW[p],
searchAreaH[p]);
} else {
peak = PivUtil.getGaussianPeak(
PlanarImage.wrapRenderedImage(corr[r]), intWinW[p] / 2
- searchAreaW[p] / 2, intWinH[p] / 2
- searchAreaH[p] / 2, searchAreaW[p],
searchAreaH[p]);
}
// valid
if (peak[2] != -1) {
shift[r][2] += peak[0] - ((double) intWinW[p]) / 2.0;
shift[r][3] += peak[1] - ((double) intWinH[p]) / 2.0;
}
// invalid
else {
shift[r][2] = 0;
shift[r][3] = 0;
}
shift[r][4] = peak[2];
}
return (new PivData(shift));
}
private void initVariables() {
settings = jpiv.getSettings();
jpiv2.PivImg img = new PivImg(jpiv, files[0]);
double imgWidth = img.getWidth();
double imgHeight = img.getHeight();
if (settings.pivSequence == Settings.PIV_TWO_IMG)
imgHeight = imgHeight / 2;
if (settings.pivUseImageBaseName) {
destFileName = files[0];
int index = destFileName.lastIndexOf('.');
if (index != -1)
destFileName = destFileName.substring(0, index);
}
if (settings.pivUseDefaultDestFileName) {
destFileName = settings.pivDefaultDestFileName;
}
passes = settings.pivMultiPass;
intWinW = settings.pivWindow[0];
intWinH = settings.pivWindow[1];
searchAreaW = settings.pivWindow[2];
searchAreaH = settings.pivWindow[3];
horVecSpacing = settings.pivWindow[4];
verVecSpacing = settings.pivWindow[5];
horPreShift = settings.pivHorPreShift;
verPreShift = settings.pivVerPreShift;
if (settings.pivROI) {
x0 = settings.pivROIP1x + intWinW[0] / 2;
y0 = settings.pivROIP1y + intWinH[0] / 2;
x1 = settings.pivROIP1x
+ (int) ((settings.pivROIP2x - settings.pivROIP1x) / intWinW[0])
* intWinW[0] - intWinW[0] / 2;
y1 = settings.pivROIP1y
+ (int) ((settings.pivROIP2y - settings.pivROIP1y) / intWinH[0])
* intWinH[0] - intWinH[0] / 2;
} else {
x0 = intWinW[0] / 2;
y0 = intWinH[0] / 2;
x1 = (int) (imgWidth / intWinW[0]) * intWinW[0] - intWinW[0] / 2;
y1 = (int) (imgHeight / intWinH[0]) * intWinH[0] - intWinH[0] / 2;
}
initCorrMap();
initPreShift();
// decimal format for destination file numbering
int digits = 1 + (int) (Math.log(files.length) / Math.log(10));
String format = new String();
for (int i = 0; i < digits; i++)
format += "0";
fileNumFormat = new java.text.DecimalFormat(format);
}
private void initPreShift() {
jpiv2.PivData pivData = new jpiv2.PivData(x0, y0, horVecSpacing[0],
verVecSpacing[0], nx, ny, horPreShift, verPreShift, 0);
shift = pivData.getPivDataShift();
}
private void initCorrMap() {
nx = (x1 - x0) / horVecSpacing[p] + 1;
ny = (y1 - y0) / verVecSpacing[p] + 1;
corr = new PlanarImage[nx * ny];
Float[] bandValues = new Float[1];
bandValues[0] = 0f;
ParameterBlock pb = new ParameterBlock();
pb.add(new Float(intWinW[p]));
pb.add(new Float(intWinH[p]));
pb.add(bandValues);
for (int c = 0; c < corr.length; c++) {
corr[c] = JAI.create("constant", pb);
}
}
private boolean chooseDestFileName() {
jpiv2.FlexFileChooser flexFileChooser = jpiv.getFlexFileChooser();
flexFileChooser.setFiletype(FlexFileChooser.JVC, false);
int approve = flexFileChooser.showSaveDialog(jpiv);
if (approve == javax.swing.JFileChooser.APPROVE_OPTION) {
destFileName = flexFileChooser.getSelectedFile().toString();
int index = destFileName.lastIndexOf('.');
if (index != -1)
destFileName = destFileName.substring(0, index);
return (true);
} else
return (false);
}
}