Java tutorial
/* * Copyright (c) 2014-2015 Genome Research Ltd. * * Author: Mouse Informatics Group <team110g@sanger.ac.uk> * This file is part of Trend Line Cropper. * * Trend Line Cropper is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero 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 Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.sanger.mig.xray.trendlinecropper.utils; import net.imglib2.RandomAccess; import net.imglib2.meta.ImgPlus; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import org.apache.commons.math3.linear.MatrixUtils; import org.apache.commons.math3.linear.RealMatrix; import org.knime.core.node.NodeLogger; import uk.ac.sanger.mig.analysis.maths.trendline.ExpTrendLine; import uk.ac.sanger.mig.analysis.maths.trendline.Fitting; import uk.ac.sanger.mig.analysis.maths.trendline.LogTrendLine; import uk.ac.sanger.mig.analysis.maths.trendline.OLSTrendLine; import uk.ac.sanger.mig.analysis.maths.trendline.PolyTrendLine; import uk.ac.sanger.mig.analysis.maths.trendline.PowerTrendLine; import uk.ac.sanger.mig.analysis.nodetools.Image; import uk.ac.sanger.mig.xray.trendlinecropper.TrendLineCropperNodeModel; /** * Wraps the logic to crop the region around the trend line * * @author pi1 pi1@sanger.ac.uk */ public class TrendCropper<T extends RealType<T> & NativeType<T>> { // the logger instance private static final NodeLogger logger = NodeLogger.getLogger(TrendLineCropperNodeModel.class); private final int leftMargin, rightMargin, startRow, endRow, topLeftMargin, topRightMargin; private final boolean cropTop; private final static int FORWARD = 0, BACKWARD = 1; /** * * @param leftMargin * how far to the left to crop out pixels * @param rightMargin * how far to the right to crop out pixels * @param startRow * row to start cropping from * @param endRow * last row to start cropping from. -1 is end of image * @param topRightMargin * @param topLeftMargin * @param cropTop */ public TrendCropper(int leftMargin, int rightMargin, int startRow, int endRow, boolean cropTop, int topLeftMargin, int topRightMargin) { this.leftMargin = leftMargin; this.rightMargin = rightMargin; this.startRow = startRow; this.endRow = endRow; this.topLeftMargin = topLeftMargin; this.topRightMargin = topRightMargin; this.cropTop = cropTop; } /** * Crop out a region of the image following the trend line. * * @param inImage * Image that will be cropped. Original image is not modified * @param coefs * coeficients that are used to predict points of the trendline * @param trendType * the trend type, produced by the Trend Line node * @return image with specified region cropped out */ public ImgPlus<T> process(ImgPlus<T> inImage, String coefs, String trendType) { final ImgPlus<T> image = inImage.copy(); final long cols = image.dimension(Image.COL); final long rows = image.dimension(Image.ROW); // if user didn't set the ending row, will use the last row final int actualEndRow = (int) ((endRow == -1) ? rows : endRow); final OLSTrendLine trend = getTrendType(trendType, convertCoefs(coefs)); // random access to traverse the image vertically final RandomAccess<T> ra = image.randomAccess(); // random access to travel anywhere in the image to modify the pixel // values final RandomAccess<T> modRa = ra.copyRandomAccess(); ra.setPosition(startRow, Image.ROW); ra.setPosition(0, Image.COL); final int firstY = ra.getIntPosition(Image.ROW); final int firstX = (int) trend.predict(firstY); try { while (ra.getIntPosition(Image.ROW) != actualEndRow) { final int y = ra.getIntPosition(Image.ROW); final int x = (int) trend.predict(y); // set position to predicted x and y modRa.setPosition(x, Image.COL); modRa.setPosition(y, Image.ROW); // delete pixels to the right of the trend line setPixelsIn(modRa, Image.COL, (x + rightMargin), 0, FORWARD, (int) cols); // delete pixels to the left of the trend line setPixelsIn(modRa, Image.COL, (x - leftMargin), 0, BACKWARD, (int) cols); ra.fwd(Image.ROW); } } catch (ArrayIndexOutOfBoundsException e) { // shouldn't get here logger.debug(e.getStackTrace()); logger.fatal("Critical error analysing one of the images."); } if (cropTop) cropTop(image, firstY, firstX); return image; } /** * Crops the top of the spine using the first row of the trend line function. * Uses the user specified left and right margins. * * @param image * @param firstX * @param firstY */ private void cropTop(ImgPlus<T> image, int lastRow, int column) { final long cols = image.dimension(Image.COL); // random access to traverse the image vertically final RandomAccess<T> ra = image.randomAccess(); // random access to travel anywhere in the image to modify the pixel // values final RandomAccess<T> modRa = ra.copyRandomAccess(); ra.setPosition(0, Image.ROW); ra.setPosition(0, Image.COL); while (ra.getIntPosition(Image.ROW) != lastRow) { final int y = ra.getIntPosition(Image.ROW); // set position to x and y modRa.setPosition(column, Image.COL); modRa.setPosition(y, Image.ROW); // delete pixels to the right of the trend line setPixelsIn(modRa, Image.COL, (column + topLeftMargin), 0, FORWARD, (int) cols); // delete pixels to the left of the trend line setPixelsIn(modRa, Image.COL, (column - topLeftMargin), 0, BACKWARD, (int) cols); ra.fwd(Image.ROW); } } /** * Can set pixel values in a row or column, avoid "out of bounds" exceptions. * Used for deleting pixels on the side of the trend line. * * @param ra to access the pixels * @param in Image.ROW or Image.COL usually * @param until will set pixels until this ra position condition is met * @param set what value to set the pixels * @param way go {@link #BACKWARD} or {@link #FORWARD}? * @param avoid last row/column, if reached, will break loop (protects from exceptions) */ private void setPixelsIn(RandomAccess<T> ra, int in, int until, int set, int way, int avoid) { while (ra.getIntPosition(in) != until) { ra.get().setReal(set); if (way == FORWARD) { // if the line goes very close to the right edge, it might try // to write black pixels there, to avoid that just break, as // we have reached the end if ((ra.getIntPosition(in) + 1) > avoid) break; } else { // if the line goes very close to the left edge, it may go // to negative coordinates. Break to avoid this. if ((ra.getIntPosition(in) - 1) < 0) break; } if (way == FORWARD) { ra.fwd(in); } else { ra.bck(in); } } } /** * Converts the trend type specified in the table into a trend line object * and sets the provided ceofs * * @param trendType * in form a string * @param coef * in form of a matrix * @return */ private OLSTrendLine getTrendType(String trendType, RealMatrix coef) { Fitting trend = null; int degree = 0; // special case for polynomial trend as we need the degree as well if (trendType.contains("Poly")) { final String[] parts = trendType.split(":"); degree = Integer.parseInt(parts[1]); trend = Fitting.whereName(parts[0]); } else { trend = Fitting.whereName(trendType); } OLSTrendLine trendLine = null; switch (trend) { case EXP: trendLine = new ExpTrendLine(); break; case LOG: trendLine = new LogTrendLine(); break; case POLY: trendLine = new PolyTrendLine(degree); break; case POWER: trendLine = new PowerTrendLine(); break; default: throw new IllegalArgumentException( "Non-existant fitting type. Either table was modified or something went terribly wrong."); } trendLine.setCoef(coef); return trendLine; } /** Converts coefs into a matrix */ private RealMatrix convertCoefs(String coefs) { final String[] split = coefs.split(","); final double[] vals = new double[split.length]; for (int i = 0; i < split.length; i++) { vals[i] = Double.parseDouble(split[i]); } return MatrixUtils.createColumnRealMatrix(vals); } }