/*
* 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;
import icy.image.IcyBufferedImage;
import icy.roi.ROI2DArea;
import icy.sequence.Sequence;
import icy.type.TypeUtil;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import javax.vecmath.Point3i;
import plugins.nherve.toolbox.image.feature.region.IcyPixel;
import plugins.nherve.toolbox.image.mask.Mask;
import plugins.nherve.toolbox.image.mask.MaskException;
import plugins.nherve.toolbox.image.segmentation.Segmentation;
import plugins.nherve.toolbox.image.toolboxes.MorphologyToolbox;
import plugins.nherve.toolbox.image.toolboxes.SomeImageTools;
/**
* The Class BinaryIcyBufferedImage.
*
* @author Nicolas HERVE - nicolas.herve@pasteur.fr
*/
public class BinaryIcyBufferedImage extends IcyBufferedImage {
/** The Constant FALSE. */
public static final byte FALSE = (byte) 0;
/** The Constant TRUE. */
public static final byte TRUE = (byte) 255;
/** The raw binary data. */
private transient byte[] rawBinaryData;
/**
* Instantiates a new binary icy buffered image.
*
* @param width
* the width
* @param height
* the height
*/
public BinaryIcyBufferedImage(int width, int height) {
this(width, height, false);
}
public BinaryIcyBufferedImage(int width, int height, boolean value) {
super(width, height, 1, TypeUtil.TYPE_BYTE);
rawBinaryData = getRawData();
Arrays.fill(rawBinaryData, value ? TRUE : FALSE);
}
/**
* Union.
*
* @param m1
* the m1
* @param m2
* the m2
* @return the double
*/
public static double union(IcyBufferedImage m1, IcyBufferedImage m2) {
double i = 0;
byte[] raw1 = m1.getDataXYAsByte(0);
byte[] raw2 = m2.getDataXYAsByte(0);
for (int idx = 0; idx < raw1.length; idx++) {
if ((raw1[idx] == BinaryIcyBufferedImage.TRUE) || (raw2[idx] == BinaryIcyBufferedImage.TRUE)) {
i++;
}
}
return i;
}
/**
* Intersection.
*
* @param m1
* the m1
* @param m2
* the m2
* @return the double
*/
public static double intersection(BinaryIcyBufferedImage m1, BinaryIcyBufferedImage m2) {
double i = 0;
byte[] raw1 = m1.getRawData();
byte[] raw2 = m2.getRawData();
for (int idx = 0; idx < raw1.length; idx++) {
if ((raw1[idx] == BinaryIcyBufferedImage.TRUE) && (raw2[idx] == BinaryIcyBufferedImage.TRUE)) {
i++;
}
}
return i;
}
/**
* As segmentation.
*
* @param label
* the label
* @return the segmentation
* @throws MaskException
* the mask exception
*/
public Segmentation asSegmentation(String label) throws MaskException {
int w = getWidth();
int h = getHeight();
Segmentation seg = new Segmentation(w, h);
List<My2DConnectedComponent> ccs = SomeImageTools.findConnectedComponents(this);
int idx = 1;
for (My2DConnectedComponent cc : ccs) {
Mask sm = seg.createNewMask(label + " " + idx, false, Color.BLACK, 1);
sm.setBinaryData(cc.asBinaryImage(w, h, 0, 0));
idx++;
}
DifferentColorsMap colorMap = new DifferentColorsMap(seg.size());
for (Mask m : seg) {
m.setColor(colorMap.get(m.getId()));
}
seg.createBackgroundMask("Background", Color.BLACK);
seg.createIndex();
return seg;
}
public IcyBufferedImage asIcyBufferedImage(int nbc, boolean toWhite) {
Color bgc = Color.WHITE;
Color fgc = Color.BLACK;
if (toWhite) {
fgc = Color.WHITE;
bgc = Color.BLACK;
}
int w = getWidth();
int h = getHeight();
byte[] raw = getRawData();
IcyBufferedImage toSave = new IcyBufferedImage(w, h, nbc, TypeUtil.TYPE_BYTE);
Graphics2D binGraphics = toSave.createGraphics();
binGraphics.setColor(bgc);
binGraphics.fillRect(0, 0, w, h);
binGraphics.setColor(fgc);
int idx = 0;
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
if (raw[idx] == BinaryIcyBufferedImage.TRUE) {
binGraphics.fillRect(i, j, 1, 1);
}
idx++;
}
}
return toSave;
}
/**
* As ro i2 d area.
*
* @param seq
* the seq
* @return the rO i2 d area
*/
public ROI2DArea asROI2DArea(Sequence seq) {
if (getSurface() == 0) {
return null;
}
ROI2DArea asa = new ROI2DArea(new Point2D.Float(0, 0));
int w = getWidth();
int h = getHeight();
int minX = w;
int maxX = 0;
int minY = h;
int maxY = 0;
int idx = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (rawBinaryData[idx] == TRUE) {
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
if (y < minY) {
minY = y;
}
if (y > maxY) {
maxY = y;
}
}
idx++;
}
}
int bbw = maxX - minX + 1;
int bbh = maxY - minY + 1;
Rectangle bbx = new Rectangle(minX, minY, bbw, bbh);
boolean[] data = new boolean[bbw * bbh];
Arrays.fill(data, false);
for (int yo = 0; yo < bbh; yo++) {
for (int xo = 0; xo < bbw; xo++) {
if (rawBinaryData[xo + minX + (yo + minY) * w] == TRUE) {
data[xo + yo * bbw] = true;
}
}
}
asa.setAsBooleanMask(bbx, data);
asa.attachTo(seq);
return asa;
}
/**
* Gets the raw data.
*
* @return the raw data
*/
public byte[] getRawData() {
return getDataXYAsByte(0);
}
/**
* Sets the raw data.
*
* @param data
* the new raw data
*/
public void setRawData(byte[] data) {
setDataXYAsByte(0, data);
}
/**
* Gets the surface.
*
* @return the surface
*/
public int getSurface() {
int s = 0;
for (byte b : rawBinaryData) {
if (b == TRUE) {
s++;
}
}
return s;
}
/**
* Adds the.
*
* @param other
* the other
*/
public void add(BinaryIcyBufferedImage other) {
for (int d = 0; d < rawBinaryData.length; d++) {
if (other.rawBinaryData[d] == TRUE) {
rawBinaryData[d] = TRUE;
}
}
}
/**
* Adds the.
*
* @param shape
* the shape
*/
public void add(Shape shape) {
manageShape(shape, TRUE);
}
/**
* Removes the.
*
* @param shape
* the shape
*/
public void remove(Shape shape) {
manageShape(shape, FALSE);
}
/**
* Manage shape.
*
* @param shape
* the shape
* @param val
* the val
*/
private void manageShape(Shape shape, byte val) {
int w = getWidth();
Rectangle r = shape.getBounds();
int x1 = (int) Math.max(Math.floor(r.getMinX()), 0);
int x2 = (int) Math.min(x1 + Math.ceil(r.getWidth()), w);
int y1 = (int) Math.max(Math.floor(r.getMinY()), 0);
int y2 = (int) Math.min(y1 + Math.ceil(r.getHeight()), getHeight());
for (int x = x1; x < x2; x++) {
for (int y = y1; y < y2; y++) {
if (shape.contains(x, y)) {
rawBinaryData[x + w * y] = val;
}
}
}
}
/**
* Removes the.
*
* @param other
* the other
*/
public void remove(BinaryIcyBufferedImage other) {
for (int d = 0; d < rawBinaryData.length; d++) {
if (other.rawBinaryData[d] == TRUE) {
rawBinaryData[d] = FALSE;
}
}
}
/* (non-Javadoc)
* @see icy.image.IcyBufferedImage#getCopy()
*/
@Override
public BinaryIcyBufferedImage getCopy() {
BinaryIcyBufferedImage n = new BinaryIcyBufferedImage(getWidth(), getHeight());
System.arraycopy(rawBinaryData, 0, n.rawBinaryData, 0, rawBinaryData.length);
return n;
}
/* (non-Javadoc)
* @see icy.image.IcyBufferedImage#getDataAsByte(int, int, int)
*/
@Override
@Deprecated
public byte getDataAsByte(int x, int y, int c) {
return super.getDataAsByte(x, y, c);
}
/**
* Gets the.
*
* @param x
* the x
* @param y
* the y
* @return true, if successful
*/
public boolean get(int x, int y) {
return rawBinaryData[x + getWidth() * y] == TRUE;
}
/* (non-Javadoc)
* @see icy.image.IcyBufferedImage#setDataAsByte(int, int, int, byte)
*/
@Override
@Deprecated
public void setDataAsByte(int x, int y, int c, byte value) {
super.setDataAsByte(x, y, c, value);
}
/**
* Sets the.
*
* @param x
* the x
* @param y
* the y
* @param b
* the b
*/
public void set(int x, int y, boolean b) {
rawBinaryData[x + getWidth() * y] = b ? TRUE : FALSE;
}
/**
* Invert.
*/
public void invert() {
for (int i = 0; i < rawBinaryData.length; i++) {
if (rawBinaryData[i] == BinaryIcyBufferedImage.FALSE) {
rawBinaryData[i] = BinaryIcyBufferedImage.TRUE;
} else {
rawBinaryData[i] = BinaryIcyBufferedImage.FALSE;
}
}
}
/**
* Dilate.
*/
public void dilate() {
MorphologyToolbox.dilateInPlace(this);
}
/**
* Erode.
*/
public void erode() {
MorphologyToolbox.erodeInPlace(this);
}
/**
* Fill holes.
*/
public void fillHoles() {
MorphologyToolbox.fillHolesInPlace(this);
}
/**
* Fill hole not recursive.
*
* @param x
* the x
* @param y
* the y
*/
private void fillHoleNotRecursive(int x, int y) {
int width = getWidth();
int height = getHeight();
Queue<IcyPixel> todo = new ArrayDeque<IcyPixel>();
todo.offer(new IcyPixel(x, y));
IcyPixel pix = null;
int idx = 0;
do {
pix = todo.poll();
if (pix != null) {
x = (int) pix.x;
y = (int) pix.y;
idx = x + width * y;
if ((x >= 0) && (x < width) && (y >= 0) && (y < height) && (rawBinaryData[idx] == FALSE)) {
rawBinaryData[idx] = TRUE;
todo.offer(new IcyPixel(x + 1, y));
todo.offer(new IcyPixel(x - 1, y));
todo.offer(new IcyPixel(x, y + 1));
todo.offer(new IcyPixel(x, y - 1));
}
}
} while (pix != null);
}
/**
* Fill hole.
*
* @param x
* the x
* @param y
* the y
*/
public void fillHole(int x, int y) {
fillHoleNotRecursive(x, y);
}
/**
* Contains.
*
* @param px
* the px
* @return true, if successful
*/
public boolean contains(IcyPixel px) {
return contains((int) px.x, (int) px.y);
}
/**
* Contains.
*
* @param x
* the x
* @param y
* the y
* @return true, if successful
*/
public boolean contains(int x, int y) {
if ((x >= 0) && (x < getWidth()) && (y >= 0) && (y < getHeight())) {
return rawBinaryData[x + getWidth() * y] == TRUE;
} else {
return false;
}
}
/**
* Filter size.
*
* @param size
* the size
*/
public void filterSize(int size) {
int width = getWidth();
List<My2DConnectedComponent> ccs = SomeImageTools.findConnectedComponents(this, 0, size);
for (My2DConnectedComponent cc : ccs) {
for (Point3i pt : cc.getPoints()) {
rawBinaryData[pt.x + width * pt.y] = FALSE;
}
}
}
}