/*
* Copyright 2010, 2011 Institut Pasteur.
*
* This file is part of NHerve Main Toolbox, which is an ICY plugin.
*
* NHerve Main Toolbox 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.
*
* NHerve Main Toolbox 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 NHerve Main Toolbox. If not, see <http://www.gnu.org/licenses/>.
*/
package plugins.nherve.toolbox.image.toolboxes;
import icy.file.Saver;
import icy.image.IcyBufferedImage;
import icy.sequence.Sequence;
import icy.type.TypeUtil;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import loci.formats.FormatException;
import plugins.nherve.toolbox.Algorithm;
import plugins.nherve.toolbox.image.BinaryIcyBufferedImage;
import plugins.nherve.toolbox.image.My2DConnectedComponent;
import plugins.nherve.toolbox.image.My2DConnectedComponentFinder;
import plugins.nherve.toolbox.image.feature.fuzzy.HysteresisThresholder;
import plugins.nherve.toolbox.image.mask.Mask;
import plugins.nherve.toolbox.image.segmentation.Segmentation;
/**
* The Class SomeImageTools.
*
* @author Nicolas HERVE - nicolas.herve@pasteur.fr
*/
public class SomeImageTools {
// TODO revoir ce code pourri !
/**
* Binarize.
*
* @param img
* the img
* @param bin
* the bin
* @param rowOffset
* the row offset
* @param colOffset
* the col offset
* @param scale
* the scale
* @param thresh
* the thresh
*/
public static void binarize(IcyBufferedImage img, BinaryIcyBufferedImage bin, int rowOffset, int colOffset, int scale, double thresh) {
Raster raster = img.getRaster();
int x = 0;
int y = 0;
double t = 0;
int w = bin.getWidth();
byte[] raw = bin.getRawData();
if (img.getSampleModel().getNumBands() == 1) {
t = thresh;
for (int i = 0; i < raster.getWidth(); i += scale) {
for (int j = 0; j < raster.getHeight(); j += scale) {
int v = raster.getSample(i, j, 0);
if (v < t) {
x = (colOffset + i) / scale;
y = (rowOffset + j) / scale;
raw[x + w * y] = BinaryIcyBufferedImage.TRUE;
}
}
}
} else {
t = 3 * thresh;
for (int i = 0; i < raster.getWidth(); i += scale) {
for (int j = 0; j < raster.getHeight(); j += scale) {
int r = raster.getSample(i, j, 0);
int g = raster.getSample(i, j, 1);
int b = raster.getSample(i, j, 2);
int crit = r + g + b;
if (crit < t) {
x = (colOffset + i) / scale;
y = (rowOffset + j) / scale;
raw[x + w * y] = BinaryIcyBufferedImage.TRUE;
}
}
}
}
}
/**
* Binarize.
*
* @param img
* the img
* @param thresh
* the thresh
* @return the binary icy buffered image
*/
public static BinaryIcyBufferedImage binarize(IcyBufferedImage img, double thresh) {
int w = img.getWidth();
int h = img.getHeight();
int s = w * h;
BinaryIcyBufferedImage ibin = new BinaryIcyBufferedImage(w, h);
double[] data = img.getDataXYAsDouble(0);
byte[] bool = ibin.getDataXYAsByte(0);
for (int idx = 0; idx < s; idx++) {
if (data[idx] > thresh) {
bool[idx] = BinaryIcyBufferedImage.TRUE;
} else {
bool[idx] = BinaryIcyBufferedImage.FALSE;
}
}
return ibin;
}
public static BinaryIcyBufferedImage toMask(IcyBufferedImage img) {
int w = img.getWidth();
int h = img.getHeight();
int s = w * h;
BinaryIcyBufferedImage ibin = new BinaryIcyBufferedImage(w, h);
double[] data = img.getDataXYAsDouble(0);
byte[] bool = ibin.getDataXYAsByte(0);
for (int idx = 0; idx < s; idx++) {
if (data[idx] == 0) {
bool[idx] = BinaryIcyBufferedImage.TRUE;
} else {
bool[idx] = BinaryIcyBufferedImage.FALSE;
}
}
return ibin;
}
/**
* Binarize.
*
* @param img
* the img
* @param toSave
* the to save
* @param thresh
* the thresh
*/
public static void binarize(IcyBufferedImage img, IcyBufferedImage toSave, double thresh) {
int w = img.getWidth();
int h = img.getHeight();
int s = w * h;
double[] data = img.getDataXYAsDouble(0);
byte[] byt0 = toSave.getDataXYAsByte(0);
byte[] byt1 = toSave.getDataXYAsByte(1);
byte[] byt2 = toSave.getDataXYAsByte(2);
for (int idx = 0; idx < s; idx++) {
if (data[idx] > thresh) {
byt0[idx] = (byte) 0;
byt1[idx] = (byte) 0;
byt2[idx] = (byte) 0;
}
}
toSave.dataChanged();
}
/**
* Binarize for save.
*
* @param img
* the img
* @param thresh
* the thresh
* @return the icy buffered image
*/
public static IcyBufferedImage binarizeForSave(IcyBufferedImage img, double thresh) {
int w = img.getWidth();
int h = img.getHeight();
int s = w * h;
IcyBufferedImage toSave = new IcyBufferedImage(w, h, 3, TypeUtil.TYPE_BYTE);
double[] data = img.getDataXYAsDouble(0);
byte[] byt = toSave.getDataXYAsByte(0);
for (int idx = 0; idx < s; idx++) {
byt[idx] = (data[idx] > thresh) ? (byte) 0 : (byte) -1;
}
toSave.setDataXYAsByte(1, byt.clone());
toSave.setDataXYAsByte(2, byt.clone());
toSave.dataChanged();
return toSave;
}
/**
* Change color space.
*
* @param img
* the img
* @param cs
* the cs
* @param min
* the min
* @param max
* the max
* @return the icy buffered image
*/
public static IcyBufferedImage changeColorSpace(IcyBufferedImage img, int cs, double min, double max) {
int w = img.getWidth();
int h = img.getHeight();
IcyBufferedImage gs = new IcyBufferedImage(w, h, 3, TypeUtil.TYPE_DOUBLE);
double[][] id = new double[3][];
for (int i = 0; i < 3; i++) {
id[i] = gs.getDataXYAsDouble(i);
}
try {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
double[] ihh = ColorSpaceTools.getColorComponentsD_0_255(img, cs, x, y);
for (int i = 0; i < 3; i++) {
if (ihh[i] > max) {
id[i][x + y * w] = max;
} else if (ihh[i] < min) {
id[i][x + y * w] = min;
} else {
id[i][x + y * w] = ihh[i];
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
gs.dataChanged();
return gs;
}
/**
* Compute gray scale.
*
* @param img
* the img
* @param c
* the c
* @param n
* the n
* @return the icy buffered image
*/
public static IcyBufferedImage computeGrayScale(IcyBufferedImage img, int c, int n) {
return computeGrayScale(img, ColorSpaceTools.RGB_TO_I1H2H3, c, n);
}
/**
* Compute gray scale.
*
* @param img
* the img
* @param cs
* the cs
* @param c
* the c
* @param n
* the n
* @return the icy buffered image
*/
public static IcyBufferedImage computeGrayScale(IcyBufferedImage img, int cs, int c, int n) {
int w = img.getWidth();
int h = img.getHeight();
IcyBufferedImage gs = new IcyBufferedImage(w, h, n, TypeUtil.TYPE_DOUBLE);
ArrayList<double[]> id = new ArrayList<double[]>();
for (int i = 0; i < n; i++) {
id.add(i, gs.getDataXYAsDouble(i));
}
try {
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
double[] ihh = ColorSpaceTools.getColorComponentsD_0_255(img, cs, x, y);
for (double[] lid : id) {
lid[x + y * w] = ihh[c];
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
gs.dataChanged();
return gs;
}
// TODO � utiliser partout au lieu d'un appel direct �
// SectionConnectedComponentFinder
/**
* Find connected components.
*
* @param bin
* the bin
* @return the array list
*/
public static List<My2DConnectedComponent> findConnectedComponents(BinaryIcyBufferedImage bin) {
return findConnectedComponents(bin, 0, bin.getWidth() * bin.getHeight());
}
/**
* Find connected components.
*
* @param bin
* the bin
* @param maxCCSurface
* the max cc surface
* @param minCCSurface
* the min cc surface
* @return the array list
*/
public static List<My2DConnectedComponent> findConnectedComponents(BinaryIcyBufferedImage bin, int minCCSurface, int maxCCSurface) {
My2DConnectedComponentFinder finder = new My2DConnectedComponentFinder(bin, minCCSurface, maxCCSurface);
ArrayList<My2DConnectedComponent> ccs = new ArrayList<My2DConnectedComponent>();
for (My2DConnectedComponent cc : finder) {
ccs.add(cc);
}
return ccs;
}
/**
* Gets the bilinear interpolated rgb values.
*
* @param img
* the img
* @param x
* the x
* @param y
* the y
* @return the bilinear interpolated rgb values
*/
public static double[] getBilinearInterpolatedRGBValues(IcyBufferedImage img, double x, double y) {
int w = img.getWidth();
int h = img.getHeight();
double[] rgb = new double[3];
for (int i = 0; i < 3; i++) {
rgb[i] = getBilinearInterpolatedValue(img.getDataXYAsDouble(i), w, h, x, y);
}
return rgb;
}
/**
* Gets the bilinear interpolated value.
*
* @param data
* the data
* @param w
* the w
* @param h
* the h
* @param x
* the x
* @param y
* the y
* @return the bilinear interpolated value
*/
public static double getBilinearInterpolatedValue(final double[] data, int w, int h, double x, double y) {
if ((x < 0) || (x > w - 1) || (y < 0) || (y > h - 1)) {
return 0;
}
int x1 = (int) Math.floor(x);
int y1 = (int) Math.floor(y);
if ((x == x1) && (y == y1)) {
return data[x1 + y1 * w];
}
int x2 = x1 + 1;
int y2 = y1 + 1;
if (x == x1) {
x2 = x1;
}
if (y == y1) {
y2 = y1;
}
double dx = x2 - x;
double dy = y2 - y;
double dxdy = dx * dy;
double a1 = dx - dxdy;
double a2 = 1 + dxdy - dx - dy;
double a3 = dy - dxdy;
double a4 = dxdy;
try {
return a1 * data[x1 + y2 * w] + a2 * data[x2 + y2 * w] + a3 * data[x2 + y1 * w] + a4 * data[x1 + y1 * w];
} catch (ArrayIndexOutOfBoundsException e) {
Algorithm.err("Error for (" + x + ", " + y + ") : " + x1 + "-" + x2 + ", " + y1 + "-" + y2 + " / (" + w + ", " + h + ")");
return 0;
}
}
/**
* Gets the bilinear interpolated value.
*
* @param img
* the img
* @param canal
* the canal
* @param x
* the x
* @param y
* the y
* @return the bilinear interpolated value
*/
public static double getBilinearInterpolatedValue(IcyBufferedImage img, int canal, double x, double y) {
int w = img.getWidth();
int h = img.getHeight();
final double[] data = img.getDataXYAsDouble(canal);
return getBilinearInterpolatedValue(data, w, h, x, y);
}
/**
* Make area.
*
* @param bin
* the bin
* @return the area
*/
public static Area makeArea(BinaryIcyBufferedImage bin) {
Area area = new Area();
ArrayList<Line2D> lines = scanLine(bin);
float total = lines.size();
float step = 10f;
float nextStep = 0;
float done = 0;
float pct = 0;
for (Line2D l : lines) {
pct = done * 100 / total;
while (pct >= nextStep) {
Algorithm.out("Transforming : " + (int) pct + " % done");
nextStep += step;
}
Shape shape = new Rectangle2D.Float((float) l.getX1(), (float) l.getY1(), (float) l.getX2() - (float) l.getX1(), 1f);
area.add(new Area(shape));
done += 1;
}
Algorithm.out("Transforming : done");
return area;
}
/**
* Make area.
*
* @param img
* the img
* @param thresh
* the thresh
* @return the area
*/
public static Area makeArea(IcyBufferedImage img, int thresh) {
BinaryIcyBufferedImage bin = new BinaryIcyBufferedImage(img.getWidth(), img.getHeight());
binarize(img, bin, 0, 0, 1, thresh);
return makeArea(bin);
}
/**
* Make binary.
*
* @param area
* the area
* @param w
* the w
* @param h
* the h
* @return the binary icy buffered image
*/
public static BinaryIcyBufferedImage makeBinary(Area area, int w, int h) {
BinaryIcyBufferedImage bin = new BinaryIcyBufferedImage(w, h);
float step = 10f;
float nextStep = 0;
float pct = 0;
int idx = 0;
byte[] raw = bin.getRawData();
for (int y = 0; y < h; y++) {
pct = y * 100 / h;
while (pct >= nextStep) {
Algorithm.out("Transforming : " + (int) pct + " % done");
nextStep += step;
}
for (int x = 0; x < w; x++) {
if (area.contains(x, y)) {
raw[idx] = BinaryIcyBufferedImage.TRUE;
}
idx++;
}
}
Algorithm.out("Transforming : done");
return bin;
}
/**
* Make binary and dilate.
*
* @param img
* the img
* @param scale
* the scale
* @param thresh
* the thresh
* @return the binary icy buffered image
*/
public static BinaryIcyBufferedImage makeBinaryAndDilate(IcyBufferedImage img, int scale, int thresh) {
int binW = (int) Math.ceil(img.getWidth() / (double) scale);
int binH = (int) Math.ceil(img.getHeight() / (double) scale);
BinaryIcyBufferedImage bin = new BinaryIcyBufferedImage(binW, binH);
binarize(img, bin, 0, 0, scale, thresh);
MorphologyToolbox.fillHolesInPlace(bin);
MorphologyToolbox.dilateInPlace(bin);
return bin;
}
/**
* Rotate.
*
* @param aDrawing
* the a drawing
* @param angle
* the angle
* @return the icy buffered image
*/
public static IcyBufferedImage rotate(IcyBufferedImage aDrawing, double angle) {
AffineTransform at = new AffineTransform();
at.rotate(angle);
Rectangle2D rectangle2D = new Rectangle2D.Float(0, 0, aDrawing.getWidth(), aDrawing.getHeight());
Rectangle2D transformedRectangle = at.createTransformedShape(rectangle2D).getBounds2D();
IcyBufferedImage rotatedDrawing = new IcyBufferedImage((int) transformedRectangle.getWidth(), (int) transformedRectangle.getHeight(), aDrawing.getColorModel().getNumColorComponents(), aDrawing.getColorModel().getTransferType());
Graphics2D g = rotatedDrawing.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, rotatedDrawing.getWidth(), rotatedDrawing.getHeight());
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.translate(-transformedRectangle.getMinX(), -transformedRectangle.getMinY());
g.rotate(angle);
g.drawImage(aDrawing, null, 0, 0);
return rotatedDrawing;
}
/**
* Saliency.
*
* @param dtct
* the dtct
* @param max
* the max
* @param sigma
* the sigma
* @return the icy buffered image
*/
public static IcyBufferedImage saliency(IcyBufferedImage dtct, double max, double sigma) {
int w = dtct.getWidth();
int h = dtct.getHeight();
int sz = (int) Math.ceil(3 * sigma);
int dim = 2 * sz + 1;
double[] g = new double[dim * dim];
double cst = max / (sigma * Math.sqrt(2 * Math.PI));
int idx = 0;
// double norm = 0;
for (int y = -sz; y <= sz; y++) {
for (int x = -sz; x <= sz; x++) {
double dst = Math.sqrt(x * x + y * y) / sigma;
g[idx] = cst * Math.exp(-0.5 * dst * dst);
// norm += g[idx];
idx++;
}
}
IcyBufferedImage res = new IcyBufferedImage(w, h, 1, TypeUtil.TYPE_DOUBLE);
double[] resData = res.getDataXYAsDouble(0);
double[] dtctData = dtct.getDataXYAsDouble(0);
Arrays.fill(resData, 0.0);
int off = 0;
for (int y = 0; y < h; y++) {
off = y * w;
for (int x = 0; x < w; x++) {
if (dtctData[off] > 0) {
idx = 0;
for (int dy = -sz; dy <= sz; dy++) {
for (int dx = -sz; dx <= sz; dx++) {
int nx = x + dx;
int ny = y + dy;
int idx2 = nx + w * ny;
if ((nx >= 0) && (nx < w) && (ny >= 0) && (ny < h)) {
resData[idx2] += g[idx] * dtctData[off];
}
idx++;
}
}
}
off++;
}
}
res.dataChanged();
return res;
}
/**
* Process.
*
* @param img
* the img
* @param sm
* the sm
* @param ss
* the ss
* @param hl
* the hl
* @param hh
* the hh
* @return the icy buffered image
*/
public static IcyBufferedImage saliencyAndHysteresis(IcyBufferedImage img, double sm, double ss, double hl, double hh) {
return saliencyAndHysteresis(img, sm, ss, hl, hh, null);
}
/**
* Process.
*
* @param img
* the img
* @param sm
* the sm
* @param ss
* the ss
* @param hl
* the hl
* @param hh
* the hh
* @param output
* the output
* @return the icy buffered image
*/
public static IcyBufferedImage saliencyAndHysteresis(IcyBufferedImage img, double sm, double ss, double hl, double hh, Sequence output) {
IcyBufferedImage sal = saliency(img, sm, ss);
if (output != null) {
output.setImage(0, 0, sal);
}
HysteresisThresholder hta = new HysteresisThresholder(hh, hl);
IcyBufferedImage hyst = hta.work(sal);
return hyst;
}
/**
* Save.
*
* @param bin
* the bin
* @param binaryFile
* the binary file
* @param nbc
* the nbc
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void save(BinaryIcyBufferedImage bin, File binaryFile, int nbc) throws IOException {
IcyBufferedImage toSave = bin.asIcyBufferedImage(nbc, false);
try {
Saver.saveImage(toSave, binaryFile, true);
} catch (FormatException e) {
throw new IOException(e);
}
}
/**
* Save.
*
* @param seg
* the seg
* @param tiffFile
* the tiff file
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public static void save(Segmentation seg, File tiffFile) throws IOException {
int w = seg.getWidth();
int h = seg.getHeight();
IcyBufferedImage img = new IcyBufferedImage(w, h, ColorSpaceTools.NB_COLOR_CHANNELS, TypeUtil.TYPE_BYTE);
Graphics2D g = img.createGraphics();
g.setBackground(Color.WHITE);
g.setColor(Color.WHITE);
g.fillRect(0, 0, w, h);
for (Mask m : seg) {
g.setColor(m.getColor());
byte[] raw = m.getBinaryData().getRawData();
int idx = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
if (raw[idx] == BinaryIcyBufferedImage.TRUE) {
g.fillRect(i, j, 1, 1);
}
idx++;
}
}
}
try {
Saver.saveImage(img, tiffFile, true);
} catch (FormatException e) {
throw new IOException(e);
}
}
/**
* Scan line.
*
* @param bin
* the bin
* @return the array list
*/
public static ArrayList<Line2D> scanLine(BinaryIcyBufferedImage bin) {
ArrayList<Line2D> lines = new ArrayList<Line2D>();
int w = bin.getWidth();
int h = bin.getHeight();
int sx = -1;
byte[] binraw = bin.getRawData();
int idx = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (binraw[idx] == BinaryIcyBufferedImage.TRUE) {
if (sx == -1) {
sx = x;
}
} else {
if (sx >= 0) {
lines.add(new Line2D.Float(sx, y, x, y));
sx = -1;
}
}
idx++;
}
if (sx >= 0) {
lines.add(new Line2D.Float(sx, y, w, y));
sx = -1;
}
}
return lines;
}
/**
* To image.
*
* @param bin
* the bin
* @return the icy buffered image
*/
public static IcyBufferedImage toImage(BinaryIcyBufferedImage bin) {
IcyBufferedImage toSave = new IcyBufferedImage(bin.getWidth(), bin.getHeight(), 1, TypeUtil.TYPE_DOUBLE);
double[] ddt = toSave.getDataXYAsDouble(0);
byte[] bdt = bin.getRawData();
for (int i = 0; i < bdt.length; i++) {
if (bdt[i] == BinaryIcyBufferedImage.TRUE) {
ddt[i] = 1d;
} else {
ddt[i] = 0d;
}
}
return toSave;
}
public static BufferedImage resize(BufferedImage original, int w, int h, boolean keepOrigianlType) {
return ImageTools.resize(original, w, h, keepOrigianlType);
}
public static BufferedImage resize(BufferedImage original, int w, int h) {
return ImageTools.resize(original, w, h);
}
public static void resizeAndDraw(BufferedImage original, Graphics2D g2, int w, int h) {
ImageTools.resizeAndDraw(original, g2, w, h);
}
public static void resizeAndDraw(BufferedImage original, Graphics2D g2, int w, int h, int x, int y) {
ImageTools.resizeAndDraw(original, g2, w, h, x, y);
}
}