package cz.cuni.lf1.lge.ThunderSTORM.util; import ij.process.FloatProcessor; import static cz.cuni.lf1.lge.ThunderSTORM.util.MathProxy.pow; import ij.process.ByteProcessor; import ij.process.ImageStatistics; import java.util.Arrays; /** * Helper class to offer some additional functionality over the ImageProcessor from ImageJ. */ public class ImageMath { public static FloatProcessor ones(int width, int height) { float [] matrix = new float[height*width]; Arrays.fill(matrix, 1f); return new FloatProcessor(width, height, matrix, null); } /** * Add two images. * * The images are required to be of the same size. * * @param fp1 an input image which the other input image ({@code fp2}) will be added to * @param fp2 another input image which will be added to {@code fp1} * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fp3 = fp1 + fp2} */ public static FloatProcessor add(FloatProcessor fp1, FloatProcessor fp2) { assert (fp1.getWidth() == fp2.getWidth()); assert (fp1.getHeight() == fp2.getHeight()); FloatProcessor out = new FloatProcessor(fp1.getWidth(), fp1.getHeight()); out.setMask(fp1.getMask() != null ? fp1.getMask(): fp2.getMask()); for (int i = 0, im = fp1.getWidth(); i < im; i++) { for (int j = 0, jm = fp1.getHeight(); j < jm; j++) { out.setf(i, j, fp1.getPixelValue(i, j) + fp2.getPixelValue(i, j)); } } return out; } /** * Add a scalar value to an image. * * @param val an input value will be added to the input image ({@code fp}) * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = val + fp} */ public static FloatProcessor add(float val, FloatProcessor fp) { FloatProcessor out = new FloatProcessor(fp.getWidth(), fp.getHeight()); out.setMask(fp.getMask()); for (int i = 0, im = fp.getWidth(); i < im; i++) { for (int j = 0, jm = fp.getHeight(); j < jm; j++) { out.setf(i, j, val + fp.getPixelValue(i, j)); } } return out; } /** * Subtract one image to the other. * * The images are required to be of the same size. * * @param fp1 an input image which the other input image ({@code fp2}) will be subtracted from * @param fp2 another input image which will be subtracted from {@code fp1} * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fp3 = fp1 - fp2} */ public static FloatProcessor subtract(FloatProcessor fp1, FloatProcessor fp2) { assert (fp1.getWidth() == fp2.getWidth()); assert (fp1.getHeight() == fp2.getHeight()); FloatProcessor out = new FloatProcessor(fp1.getWidth(), fp1.getHeight()); out.setMask(fp1.getMask() != null ? fp1.getMask(): fp2.getMask()); for (int i = 0, im = fp1.getWidth(); i < im; i++) { for (int j = 0, jm = fp1.getHeight(); j < jm; j++) { out.setf(i, j, fp1.getPixelValue(i, j) - fp2.getPixelValue(i, j)); } } return out; } /** * Subtract an image from a scalar. * * @param val an input value which the input image ({@code fp}) will be subtracted from * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = val - fp} */ public static FloatProcessor subtract(float val, FloatProcessor fp) { FloatProcessor out = new FloatProcessor(fp.getWidth(), fp.getHeight()); out.setMask(fp.getMask()); for (int i = 0, im = fp.getWidth(); i < im; i++) { for (int j = 0, jm = fp.getHeight(); j < jm; j++) { out.setf(i, j, val - fp.getPixelValue(i, j)); } } return out; } /** * Subtract a scalar value from an image. * * @param val an input value which will be substracted from the input image ({@code fp}) * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = fp - val} */ public static FloatProcessor subtract(FloatProcessor fp, float val) { return add(-val, fp); } /** * Multiply two images. * * The images are required to be of the same size. * * @param fp1 an input image which the other input image ({@code fp2}) will be multiplied with * @param fp2 another input image which will be multiplied with {@code fp1} * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fp3 = fp1 * fp2} */ public static FloatProcessor multiply(FloatProcessor fp1, FloatProcessor fp2) { assert (fp1.getWidth() == fp2.getWidth()); assert (fp1.getHeight() == fp2.getHeight()); FloatProcessor out = new FloatProcessor(fp1.getWidth(), fp1.getHeight()); out.setMask(fp1.getMask() != null ? fp1.getMask(): fp2.getMask()); for (int i = 0, im = fp1.getWidth(); i < im; i++) { for (int j = 0, jm = fp1.getHeight(); j < jm; j++) { out.setf(i, j, fp1.getPixelValue(i, j) * fp2.getPixelValue(i, j)); } } return out; } /** * Multiply an image by a scalar value. * * @param val an input value which the input image ({@code fp}) will be multiplied with * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = val * fp} */ public static FloatProcessor multiply(float val, FloatProcessor fp) { FloatProcessor out = new FloatProcessor(fp.getWidth(), fp.getHeight()); out.setMask(fp.getMask()); for (int i = 0, im = fp.getWidth(); i < im; i++) { for (int j = 0, jm = fp.getHeight(); j < jm; j++) { out.setf(i, j, val * fp.getPixelValue(i, j)); } } return out; } /** * Divide values of one image by values of the other image. * * The images are required to be of the same size. * * @param fp1 an input image which the other input image ({@code fp2}) will be divided by * @param fp2 another input image which will divide the {@code fp1} * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fp3 = fp1 / fp2} */ public static FloatProcessor divide(FloatProcessor fp1, FloatProcessor fp2) { assert (fp1.getWidth() == fp2.getWidth()); assert (fp1.getHeight() == fp2.getHeight()); FloatProcessor out = new FloatProcessor(fp1.getWidth(), fp1.getHeight()); out.setMask(fp1.getMask() != null ? fp1.getMask(): fp2.getMask()); for (int i = 0, im = fp1.getWidth(); i < im; i++) { for (int j = 0, jm = fp1.getHeight(); j < jm; j++) { out.setf(i, j, fp1.getPixelValue(i, j) / fp2.getPixelValue(i, j)); } } return out; } /** * Divide a scalar value by values from an image. * * @param val an input value which the input image ({@code fp}) will divide * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = val / fp} */ public static FloatProcessor divide(float val, FloatProcessor fp) { FloatProcessor out = new FloatProcessor(fp.getWidth(), fp.getHeight()); out.setMask(fp.getMask()); for (int i = 0, im = fp.getWidth(); i < im; i++) { for (int j = 0, jm = fp.getHeight(); j < jm; j++) { out.setf(i, j, val / fp.getPixelValue(i, j)); } } return out; } /** * Divide an image by a scalar value. * * @param val an input value which will divide the input image ({@code fp}) * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = fp / val} */ public static FloatProcessor divide(FloatProcessor fp, float val) { return multiply(1.0f/val, fp); } /** * Calculate a {@code val}-th power of an image {@code fp}. * * @param val {@code val}-th power of {@code fp} * @param fp an input image * @return a <strong>new instance</strong> of FloatProcessor: {@mathjax fpv = fp ^ val} */ public static FloatProcessor power(FloatProcessor fp, float val) { FloatProcessor out = new FloatProcessor(fp.getWidth(), fp.getHeight()); out.setMask(fp.getMask()); for (int i = 0, im = fp.getWidth(); i < im; i++) { for (int j = 0, jm = fp.getHeight(); j < jm; j++) { out.setf(i, j, (float)pow((double)fp.getPixelValue(i, j), (double)val)); } } return out; } /** * Apply a binary {@code threshold} to the {@code image}. * * Instead of implicitly setting the pixels in thresholded image to 0 and 1, * the pixels with their values equal or greater than threshold are set * to {@code high_val}. The rest is res to {@code low_value}. * * Note that this method <strong>modifies</strong> the input image. * * @param image an input image * @param threshold a threshold value * @param low_val value that the pixels with values lesser then {@code threshold} are set to * @param high_val value that the pixels with values equal or greater then {@code threshold} are set to */ public static void threshold(FloatProcessor image, float threshold, float low_val, float high_val) { for (int i = 0, im = image.getWidth(); i < im; i++) { for (int j = 0, jm = image.getHeight(); j < jm; j++) { if (image.getPixelValue(i, j) >= threshold) { image.setf(i, j, high_val); } else { image.setf(i, j, low_val); } } } } /** * Apply a {@code mask} to an input {@code image}. * * The masking works simply by checking where is a 0 in the mask image and * setting the corresponding pixel in the input image to 0 as well. Hence the * mask does not have to be binary. * * For example let's have the following input image: * <pre> * {@code * 123 * 456 * 789} * </pre> * and the following mask: * <pre> * {@code * 560 * 804 * 032} * </pre> * Then the result of applying the mask is: * <pre> * {@code * 120 * 406 * 089} * </pre> * * @param image an input image * @param mask a mask image * @return a <strong>new instance</strong> of FloatProcessor that contains the input image after the mask was applied */ public static FloatProcessor applyMask(FloatProcessor image, FloatProcessor mask) { assert (image.getWidth() == mask.getWidth()); assert (image.getHeight() == mask.getHeight()); FloatProcessor result = new FloatProcessor(image.getWidth(), image.getHeight(), (float[]) image.getPixelsCopy(), null); for (int x = 0, xm = image.getWidth(); x < xm; x++) { for (int y = 0, ym = image.getHeight(); y < ym; y++) { if (mask.getf(x, y) == 0.0f) { result.setf(x, y, 0.0f); } } } return result; } public static FloatProcessor relEq(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a<b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); for(int x = 0; x < a.getWidth(); x++) { for(int y = 0; y < a.getHeight(); y++) { res.setf(x, y, ((a.getf(x, y) == b.getf(x, y)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relNeq(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a<b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); for(int x = 0; x < a.getWidth(); x++) { for(int y = 0; y < a.getHeight(); y++) { res.setf(x, y, ((a.getf(x, y) != b.getf(x, y)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relGt(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a<b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); for(int x = 0; x < a.getWidth(); x++) { for(int y = 0; y < a.getHeight(); y++) { res.setf(x, y, ((a.getf(x, y) > b.getf(x, y)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relLt(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a<b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); for(int x = 0; x < a.getWidth(); x++) { for(int y = 0; y < a.getHeight(); y++) { res.setf(x, y, ((a.getf(x, y) < b.getf(x, y)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relLt(Double val, FloatProcessor mat) { float v = val.floatValue(); FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); res.setMask(mat.getMask()); for(int x = 0; x < mat.getWidth(); x++) { for(int y = 0; y < mat.getHeight(); y++) { res.setf(x, y, ((v < mat.getf(x, y)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relLt(FloatProcessor mat, Double val) { float v = val.floatValue(); FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); res.setMask(mat.getMask()); for(int x = 0; x < mat.getWidth(); x++) { for(int y = 0; y < mat.getHeight(); y++) { res.setf(x, y, ((mat.getf(x, y) < v) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relGt(Double val, FloatProcessor mat) { float v = val.floatValue(); FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); for(int x = 0; x < mat.getWidth(); x++) { for(int y = 0; y < mat.getHeight(); y++) { res.setf(x, y, ((v > mat.getf(x, y)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relGt(FloatProcessor mat, Double val) { float v = val.floatValue(); FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); for(int x = 0; x < mat.getWidth(); x++) { for(int y = 0; y < mat.getHeight(); y++) { res.setf(x, y, ((mat.getf(x, y) > v) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor logAnd(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a&b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); for(int x = 0; x < a.getWidth(); x++) { for(int y = 0; y < a.getHeight(); y++) { res.setf(x, y, (((a.getf(x, y) != 0.0f) && (b.getf(x, y) != 0.0f)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor logOr(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a|b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); for(int x = 0; x < a.getWidth(); x++) { for(int y = 0; y < a.getHeight(); y++) { res.setf(x, y, (((a.getf(x, y) != 0.0f) || (b.getf(x, y) != 0.0f)) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relEq(Double val, FloatProcessor mat) { float v = val.floatValue(); FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); res.setMask(mat.getMask()); for(int x = 0; x < mat.getWidth(); x++) { for(int y = 0; y < mat.getHeight(); y++) { res.setf(x, y, ((mat.getf(x, y) == v) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relEq(FloatProcessor mat, Double val) { return relEq(val, mat); } public static FloatProcessor relNeq(Double val, FloatProcessor mat) { float v = val.floatValue(); FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); res.setMask(mat.getMask()); for(int x = 0; x < mat.getWidth(); x++) { for(int y = 0; y < mat.getHeight(); y++) { res.setf(x, y, ((mat.getf(x, y) != v) ? 1.0f : 0.0f)); } } return res; } public static FloatProcessor relNeq(FloatProcessor mat, Double val) { return relNeq(val, mat); } public static FloatProcessor abs(FloatProcessor mat) { FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight(), (float [])mat.getPixelsCopy(), null); res.abs(); res.setMask(mat.getMask()); return res; } public static FloatProcessor modulo(float val, FloatProcessor mat) { FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); res.setMask(mat.getMask()); float tmp; for (int i = 0, im = mat.getWidth(); i < im; i++) { for (int j = 0, jm = mat.getHeight(); j < jm; j++) { tmp = val / mat.getf(i, j); res.setf(i, j, val - (((float)((int)tmp)) * mat.getf(i, j))); } } return res; } public static FloatProcessor modulo(FloatProcessor mat, float val) { FloatProcessor res = new FloatProcessor(mat.getWidth(), mat.getHeight()); res.setMask(mat.getMask()); float tmp; for (int i = 0, im = mat.getWidth(); i < im; i++) { for (int j = 0, jm = mat.getHeight(); j < jm; j++) { tmp = mat.getf(i, j) / val; res.setf(i, j, mat.getf(i, j) - (((float)((int)tmp)) * val)); } } return res; } public static FloatProcessor modulo(FloatProcessor a, FloatProcessor b) { if((a.getWidth() != b.getWidth()) || (a.getHeight()!= b.getHeight())) { throw new IllegalArgumentException("Error during evaluation of `a%b` expression! Both operands must be of the same size!"); } FloatProcessor res = new FloatProcessor(a.getWidth(), a.getHeight()); res.setMask(a.getMask() != null ? a.getMask(): b.getMask()); float tmp; for (int i = 0, im = a.getWidth(); i < im; i++) { for (int j = 0, jm = a.getHeight(); j < jm; j++) { tmp = a.getf(i, j) / b.getf(i, j); res.setf(i, j, a.getf(i, j) - (((float)((int)tmp)) * b.getf(i, j))); } } return res; } }