-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'newsolver' into newexport
- Loading branch information
Showing
59 changed files
with
3,594 additions
and
177 deletions.
There are no files selected for viewing
122 changes: 122 additions & 0 deletions
122
render-app/src/main/java/org/janelia/alignment/destreak/StreakFinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.janelia.alignment.destreak; | ||
|
||
import ij.IJ; | ||
import ij.ImagePlus; | ||
import ij.process.FloatProcessor; | ||
import ij.process.ImageProcessor; | ||
|
||
import java.io.Serializable; | ||
|
||
/** | ||
* This class detects streaks in an image and returns a corresponding mask. | ||
* <p> | ||
* The finder first applies a derivative filter in the x-direction to detect vertical edges. Then, it applies a mean | ||
* filter in the y-direction to smooth out the edges in the y-direction. The resulting image is then thresholded | ||
* (from above and below) to create a mask of the streaks. Finally, an optional Gaussian blur is applied to the mask to | ||
* smooth it. The mask is 0 where there are no streaks and 255 where there are streaks. | ||
* <p> | ||
* There are three parameters that can be set: | ||
* <ul> | ||
* <li>meanFilterSize: the number of pixels to average in the y-direction (e.g., 0 means no averaging, 50 means averaging +/-50 pixels in y)</li> | ||
* <li>threshold: the threshold used to convert the streak mask to a binary mask</li> | ||
* <li>blurRadius: the radius of the Gaussian blur applied to the streak mask (0 means no smoothing)</li> | ||
* </ul> | ||
*/ | ||
public class StreakFinder implements Serializable { | ||
|
||
private final int meanFilterSize; | ||
private final double threshold; | ||
private final int blurRadius; | ||
|
||
public StreakFinder(final int meanFilterSize, final double threshold, final int blurRadius) { | ||
if (meanFilterSize < 0) { | ||
throw new IllegalArgumentException("meanFilterSize must be non-negative"); | ||
} | ||
if (threshold < 0) { | ||
throw new IllegalArgumentException("threshold must be non-negative"); | ||
} | ||
if (blurRadius < 0) { | ||
throw new IllegalArgumentException("blurRadius must be 0 (no blur) or positive"); | ||
} | ||
|
||
this.meanFilterSize = meanFilterSize; | ||
this.threshold = threshold; | ||
this.blurRadius = blurRadius; | ||
} | ||
|
||
public ImagePlus createStreakMask(final ImagePlus input) { | ||
ImageProcessor filtered = differenceFilterX(input.getProcessor()); | ||
filtered = meanFilterY(filtered, meanFilterSize); | ||
filtered = bidirectionalThreshold(filtered, threshold); | ||
|
||
final ImagePlus mask = new ImagePlus("Mask", filtered); | ||
if (blurRadius > 0) { | ||
IJ.run(mask, "Gaussian Blur...", String.format("sigma=%d", blurRadius)); | ||
} | ||
return mask; | ||
} | ||
|
||
private static ImageProcessor differenceFilterX(final ImageProcessor in) { | ||
final ImageProcessor out = new FloatProcessor(in.getWidth(), in.getHeight()); | ||
final int width = in.getWidth(); | ||
final int height = in.getHeight(); | ||
|
||
for (int y = 0; y < height; y++) { | ||
for (int x = 0; x < width; x++) { | ||
final float left = in.getf(projectPeriodically(x - 1, width), y); | ||
final float right = in.getf(projectPeriodically(x + 1, width), y); | ||
out.setf(x, y, (right - left) / 2); | ||
} | ||
} | ||
return out; | ||
} | ||
|
||
private static ImageProcessor meanFilterY(final ImageProcessor in, final int size) { | ||
final ImageProcessor out = new FloatProcessor(in.getWidth(), in.getHeight()); | ||
final int width = in.getWidth(); | ||
final int height = in.getHeight(); | ||
final int n = 2 * size + 1; | ||
|
||
for (int x = 0; x < width; x++) { | ||
// initialize running sum | ||
float sum = in.getf(x, 0); | ||
for (int y = 1; y <= size; y++) { | ||
sum += 2 * in.getf(x, y); | ||
} | ||
out.setf(x, 0, sum / n); | ||
|
||
// update running sum by adding the next value and subtracting the oldest value | ||
for (int y = 1; y < height; y++) { | ||
final float oldest = in.getf(x, projectPeriodically(y - size - 1, height)); | ||
final float newest = in.getf(x, projectPeriodically(y + size, height)); | ||
sum += newest - oldest; | ||
out.setf(x, y, sum / n); | ||
} | ||
} | ||
return out; | ||
} | ||
|
||
private static ImageProcessor bidirectionalThreshold(final ImageProcessor in, final double threshold) { | ||
final ImageProcessor out = new FloatProcessor(in.getWidth(), in.getHeight()); | ||
final int width = in.getWidth(); | ||
final int height = in.getHeight(); | ||
|
||
for (int y = 0; y < height; y++) { | ||
for (int x = 0; x < width; x++) { | ||
final float value = Math.abs(in.getf(x, y)); | ||
out.setf(x, y, (value > threshold) ? 255 : 0); | ||
} | ||
} | ||
return out; | ||
} | ||
|
||
private static int projectPeriodically(final int index, final int max) { | ||
if (index < 0) { | ||
return -index; | ||
} else if (index >= max) { | ||
return 2 * max - index - 2; | ||
} else { | ||
return index; | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
render-app/src/main/java/org/janelia/alignment/filter/AffineIntensityFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package org.janelia.alignment.filter; | ||
|
||
import ij.process.ImageProcessor; | ||
|
||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* A simple filter that applies an affine transformation y = a*x + b to the intensity values of an image. | ||
*/ | ||
public class AffineIntensityFilter implements Filter { | ||
|
||
private double a; | ||
private double b; | ||
|
||
// empty constructor required to create instances from specifications | ||
@SuppressWarnings("unused") | ||
public AffineIntensityFilter() { | ||
this(0.0, 0.0); | ||
} | ||
|
||
public AffineIntensityFilter(final double a, final double b) { | ||
this.a = a; | ||
this.b = b; | ||
} | ||
|
||
@Override | ||
public void init(final Map<String, String> params) { | ||
this.a = Filter.getDoubleParameter("a", params); | ||
this.b = Filter.getDoubleParameter("b", params); | ||
} | ||
|
||
@Override | ||
public Map<String, String> toParametersMap() { | ||
final Map<String, String> map = new LinkedHashMap<>(); | ||
map.put("a", String.valueOf(a)); | ||
map.put("b", String.valueOf(b)); | ||
return map; | ||
} | ||
|
||
@Override | ||
public void process(final ImageProcessor ip, final double scale) { | ||
for (int y = 0; y < ip.getHeight(); y++) { | ||
for (int x = 0; x < ip.getWidth(); x++) { | ||
final float intensity = ip.getf(x, y); | ||
ip.setf(x, y, (float) (a * intensity + b)); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,5 +60,4 @@ public void process(final ImageProcessor ip, final double scale) { | |
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
render-app/src/main/java/org/janelia/alignment/filter/ShadingCorrectionFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package org.janelia.alignment.filter; | ||
|
||
import ij.process.ImageProcessor; | ||
import org.janelia.alignment.filter.emshading.FourthOrderShading; | ||
import org.janelia.alignment.filter.emshading.QuadraticShading; | ||
import org.janelia.alignment.filter.emshading.ShadingModel; | ||
|
||
import java.util.Arrays; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
|
||
public class ShadingCorrectionFilter implements Filter { | ||
|
||
public enum ShadingCorrectionMethod { | ||
QUADRATIC(QuadraticShading::new), | ||
FOURTH_ORDER(FourthOrderShading::new); | ||
|
||
private final Function<double[], ShadingModel> modelFactory; | ||
|
||
ShadingCorrectionMethod(final Function<double[], ShadingModel> modelFactory) { | ||
this.modelFactory = modelFactory; | ||
} | ||
|
||
public ShadingModel create(final double[] coefficients) { | ||
return modelFactory.apply(coefficients); | ||
} | ||
|
||
public static ShadingCorrectionMethod fromString(final String method) { | ||
for (final ShadingCorrectionMethod shadingCorrectionMethod : values()) { | ||
if (shadingCorrectionMethod.name().equalsIgnoreCase(method)) { | ||
return shadingCorrectionMethod; | ||
} | ||
} | ||
throw new IllegalArgumentException("Unknown shading correction method: " + method); | ||
} | ||
|
||
public static ShadingCorrectionMethod forModel(final ShadingModel model) { | ||
if (model instanceof QuadraticShading) { | ||
return QUADRATIC; | ||
} else if (model instanceof FourthOrderShading) { | ||
return FOURTH_ORDER; | ||
} else { | ||
throw new IllegalArgumentException("Unknown shading model class: " + model.getClass().getName()); | ||
} | ||
} | ||
} | ||
|
||
private ShadingCorrectionMethod correctionMethod; | ||
private double[] coefficients; | ||
|
||
// empty constructor required to create instances from specifications | ||
@SuppressWarnings("unused") | ||
public ShadingCorrectionFilter() { | ||
} | ||
|
||
public ShadingCorrectionFilter(final ShadingModel model) { | ||
this(ShadingCorrectionMethod.forModel(model), model.getCoefficients()); | ||
} | ||
|
||
public ShadingCorrectionFilter(final ShadingCorrectionMethod correctionMethod, final double[] coefficients) { | ||
this.correctionMethod = correctionMethod; | ||
this.coefficients = coefficients; | ||
} | ||
|
||
@Override | ||
public void init(final Map<String, String> params) { | ||
this.correctionMethod = ShadingCorrectionMethod.fromString(Filter.getStringParameter("correctionMethod", params)); | ||
final String[] rawCoefficients = Filter.getCommaSeparatedStringParameter("coefficients", params); | ||
this.coefficients = Arrays.stream(rawCoefficients).mapToDouble(Double::parseDouble).toArray(); | ||
} | ||
|
||
@Override | ||
public Map<String, String> toParametersMap() { | ||
final Map<String, String> map = new LinkedHashMap<>(); | ||
map.put("correctionMethod", correctionMethod.name()); | ||
map.put("coefficients", Arrays.stream(coefficients).mapToObj(String::valueOf).collect(Collectors.joining(","))); | ||
return map; | ||
} | ||
|
||
@Override | ||
public void process(final ImageProcessor ip, final double scale) { | ||
// transform pixel coordinates into [-1, 1] x [-1, 1] | ||
final double scaleX = ip.getWidth() / 2.0; | ||
final double scaleY = ip.getHeight() / 2.0; | ||
final ShadingModel shadingModel = correctionMethod.create(coefficients); | ||
|
||
// subtract shading model from image | ||
final double[] location = new double[2]; | ||
for (int i = 0; i < ip.getWidth(); i++) { | ||
for (int j = 0; j < ip.getHeight(); j++) { | ||
location[0] = ShadingModel.scaleCoordinate(i, scaleX); | ||
location[1] = ShadingModel.scaleCoordinate(j, scaleY); | ||
shadingModel.applyInPlace(location); | ||
|
||
final double value = ip.getPixelValue(i, j) - location[0]; | ||
ip.putPixelValue(i, j, value); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.