/**
*
*/
package fr.unistra.pelican.gui.MultiViews;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import fr.unistra.pelican.BooleanImage;
import fr.unistra.pelican.ByteImage;
import fr.unistra.pelican.DoubleImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.algorithms.histogram.HistogramCorrection;
import fr.unistra.pelican.algorithms.histogram.HistogramCorrection.MultiBandPolicy;
import fr.unistra.pelican.algorithms.segmentation.ManualThresholding;
import fr.unistra.pelican.util.PelicanImageToBufferedImage;
import fr.unistra.pelican.util.Tools;
import fr.unistra.pelican.util.colour.GammaCompressionModel;
import fr.unistra.pelican.util.colour.REC709GammaCompressionModel;
import fr.unistra.pelican.util.colour.SRGBGammaCompressionModel;
import fr.unistra.pelican.util.colour.SimpleGammaCompressionModel;
import fr.unistra.pelican.util.colour.GammaCompressionModel.Band;
import fr.unistra.pelican.util.mask.MaskStack;
/**
* How to display an image (zoom, translation, histogram correction, pixel scaling, colour composition, ...)
*
* @author Benjamin Perret
*
*/
public class View {
/**
* Image is currently displayed
*/
private boolean active=false;
/**
* Zoom factor on image
*/
private double zoom=1.0;
/**
* X Shift of origin (zoom factor does NOT apply on it)
*/
private int shiftX=0;
/**
* Y Shift of origin (zoom factor does NOT apply on it)
*/
private int shiftY=0;
/**
* Use auto histogram correction
*/
private boolean autoCorrect=true;
/**
* ThresholdLevel for auto correction
*/
private double autoCorrectLevel=0.99;
/**
* Multiband policy for autoCorrect
*/
private MultiBandPolicy multiBandPolicyCorrection=MultiBandPolicy.Independent;
/**
* Scale histogram to [0;1] (band independent)
*/
private boolean scaleResult=true;
/**
* Show in inverse gray scale
*/
private boolean inverseGrayScale=false;
/**
* One buffered image per band
*/
private BufferedImage [] myimg = null;
/**
* In color band only one buffered image
*/
private BufferedImage colourImg = null;
/**
* the image to display
*/
private BufferedImage display = null;
/**
* Reference to the original image
*/
private Image oriImage =null;
/**
* Internal copy (insure coherence of display if original image is modified, so changes are not reflected until refresh is called)
*/
private Image copyImage =null;
/**
* Try to follow Viewport object (if exists)
*/
private boolean followWindow=true;
/**
* Autofit in Viewport object (if exists)
*/
private boolean autoFitWindow=false;
/**
* Is the original image a Pelican Image
*/
private boolean originIsPelicanImage=true;
/**
* Use color?
*/
private boolean coloured=false;
/**
* Is not color, which band do we need to view
*/
private int displayedBand=0;
/**
* In color band which band is used for red
*/
private int colourBandR=0;
/**
* In color band which band is used for green
*/
private int colourBandG=1;
/**
* In color band which band is used for blue
*/
private int colourBandB=2;
/**
* Do we apply threshold
*/
private boolean threshold=false;
/**
* Threshold value for each band
*/
private double thresholdValue [];
public static enum GammaModel {No,Simple,sRGB,REC709};
private GammaModel gammaModel=GammaModel.No;
private GammaCompressionModel gammaCompressionModel=null;
/**
* Free to use!
*/
public Map<String,Object> properties = new TreeMap<String, Object>();
/**
* Observator view port
*/
private ViewPort viewPort;
public View(ViewPort viewPort)
{
this.viewPort=viewPort;
}
/**
*
* @return the zoom
*/
public double getZoom() {
return zoom;
}
/**
* @param zoom the zoom to set
*/
public void setZoom(double zoom) {
if(this.zoom !=zoom )fireChangeEvent();
this.zoom = zoom;
}
/**
* @return the shiftX
*/
public int getShiftX() {
return shiftX;
}
/**
* @param shiftX the shiftX to set
*/
public void setShiftX(int shiftX) {
if(shiftX!=this.shiftX)fireChangeEvent();
this.shiftX = shiftX;
}
/**
* @return the shiftY
*/
public int getShiftY() {
return shiftY;
}
/**
* @param shiftY the shiftY to set
*/
public void setShiftY(int shiftY) {
if(shiftY!=this.shiftY)fireChangeEvent();
this.shiftY = shiftY;
}
/**
* @return the autoCorrect
*/
public boolean isAutoCorrect() {
return autoCorrect;
}
/**
* @param autoCorrect the autoCorrect to set
*/
public void setAutoCorrect(boolean autoCorrect) {
this.autoCorrect = autoCorrect;
clearTempImg();
setPreProcessing();
}
/**
* @return the autoCorrectLevel
*/
public double getAutoCorrectLevel() {
return autoCorrectLevel;
}
/**
* @param autoCorrectLevel the autoCorrectLevel to set
*/
public void setAutoCorrectLevel(double autoCorrectLevel) {
this.autoCorrectLevel = Math.min(1.0,Math.max(0.0,autoCorrectLevel));
clearTempImg();
setPreProcessing();
}
/**
* @param myimg the myimg to set
*/
public void setImage(BufferedImage myimg) {
this.colourImg = myimg;
originIsPelicanImage=false;
copyImage=oriImage=bufferedImageTopelicanImage(colourImg);
checkBandsForColour();
if (copyImage.bdim==3)
{
coloured=true;
}
this.myimg=new BufferedImage[oriImage.bdim];
thresholdValue = new double[oriImage.bdim];
setPreProcessing();
}
/**
* @return the oriImage
*/
public Image getImage() {
return oriImage;
}
/**
* @return the oriImage
*/
public Image getPersistentImage() {
return copyImage;
}
/**
* @param oriImage the oriImage to set
*/
public void setImage(Image image) {
this.oriImage = image;
if (copyImage != null && image != null
&& Image.haveSameDimensions(copyImage, image)) {
copyImage.setMask(image.getMask());
for (int i = 0; i < image.size(); i++)
copyImage.setPixelDouble(i, image.getPixelDouble(i));
}
else {
copyImage = image.copyImage(true);
}
createMaskForImage(copyImage);
originIsPelicanImage=true;
thresholdValue = new double[oriImage.bdim];
myimg=new BufferedImage[oriImage.bdim];
checkBandsForColour();
if (image.bdim==3)
{
coloured=true;
}
setPreProcessing();
}
private void createMaskForImage(Image im)
{
if (im instanceof DoubleImage) {
boolean flag = true;
for (int i = 0; i < im.size() && flag; i++)
if (!Tools.isValue(im.getPixelDouble(i)))
flag = false;
if (!flag) {
BooleanImage mask = new BooleanImage(im, false);
for (int i = 0; i < mask.size(); i++)
if (Tools.isValue(im.getPixelDouble(i)))
mask.setPixelBoolean(i, true);
im.pushMask(mask);
}
}
}
public void refresh()
{
if(originIsPelicanImage)
setImage(oriImage);
else setImage(colourImg);
fireChangeEvent();
}
private Image performThreshold(Image im)
{
Image res=new BooleanImage(im.xdim,im.ydim,1,1,im.bdim);
for(int b=0;b<im.bdim;b++)
for(int y=0;y<im.ydim;y++)
for(int x=0;x<im.xdim;x++)
res.setPixelXYBBoolean(x, y, b, im.getPixelXYBDouble(x, y, b)>= thresholdValue[b]);
return res;
}
private Image temp;
private void setPreProcessing() {
Image op;
if(threshold)
{
op=performThreshold(copyImage);
}else {
op=copyImage;
}
if (!threshold && autoCorrect) {
/*
* if(myimgCorrected==null) myimgCorrected =
* VariousTools.pelicanImageToBufferedImage(HistogramCorrection.exec(oriImage));
*
*
* display=myimgCorrected;
*/
temp = HistogramCorrection.exec(op,autoCorrectLevel,HistogramCorrection.STRETCH_NOT_USE,multiBandPolicyCorrection);
} else {
/*
* if(myimg==null) myimg =
* VariousTools.pelicanImageToBufferedImage((scaleResult)?scaleToZeroOne(oriImage):oriImage);
* display=myimg;
*/
temp = (scaleResult) ? scaleToZeroOne(op) : op;
}
if (gammaCompressionModel!=null)
for (int i = 0; i < temp.size(); i++)
temp.setPixelDouble(i, gammaCompressionModel.compress(temp.getPixelDouble(i), Band.UNKNOWN));
if (inverseGrayScale) {
for (int i = 0; i < temp.size(); i++)
temp.setPixelDouble(i, 1.0 - temp.getPixelDouble(i));
}
setDisplay();
}
private void setDisplay() {
if(temp==null)
setPreProcessing();
if (coloured)
display = colourImg = PelicanImageToBufferedImage.exec(temp,
colourBandR, colourBandG, colourBandB);
else{
if(myimg[displayedBand] == null)
myimg[displayedBand] = PelicanImageToBufferedImage.exec(
temp, displayedBand);
display = myimg[displayedBand];
}
}
private Image scaleToZeroOne(Image im)
{
Image res = im.copyImage(false);
for(int b=0;b<im.bdim;b++)
{
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (int i = b; i < im.size(); i+=im.bdim)
if(im.isPresent(i)) {
double val = im.getPixelDouble(i);
if (val < min)
min = val;
if (val > max)
max = val;
}
for(int i=b;i<im.size();i+=im.bdim)
if(im.isPresent(i))
res.setPixelDouble(i, (im.getPixelDouble(i)-min)/(max-min));
}
return res;
}
public Rectangle areaCovered()
{
int xdim=viewPort.getWidth();
int ydim=viewPort.getHeight();
int xmin = getAbsoluteXCoord(0);
int ymin = getAbsoluteYCoord(0);;
int xmax = getAbsoluteXCoord(xdim)+1;
int ymax = getAbsoluteYCoord(ydim)+1;
/*if(shiftX>=0)
{
xmax=(int)Math.min((((double)(xdim+shiftX))/zoom),display.getWidth());
}else {
xmin=(int)(-shiftX/zoom);
xmax=(int)Math.min(((double)(xdim-shiftX))/zoom, display.getWidth());
}
if(shiftY>=0)
{
ymax=(int)Math.min((((double)(ydim-shiftY))/zoom),display.getHeight());
}else {
ymin=(int)(-shiftY/zoom);
ymax=(int)Math.min(((double)(ydim-shiftY))/zoom, display.getHeight());
}*/
return new Rectangle(xmin,ymin,xmax-xmin,ymax-ymin);
}
public void resetView()
{
this.zoom=1.0;
this.shiftX=0;
this.shiftY=0;
fireChangeEvent();
}
public boolean isActive(){
return active;
}
public void zoomOn(int x, int y, double coef)
{
if(coef!=1.0)
{
double orix=((double)x-(double)shiftX)/zoom;
double oriy=((double)y-(double)shiftY)/zoom;
zoom*=coef;
shiftX=(int)(x-zoom*orix);
shiftY=(int)(y-zoom*oriy);
fireChangeEvent();}
}
public void zoomOut(int x, int y, double coef)
{
zoomOn(x, y, 1.0/coef);
}
public void shiftX(int s)
{
shiftX+=s;
if(s!=0) fireChangeEvent();
}
public void shiftY(int s)
{
shiftY+=s;
if(s!=0) fireChangeEvent();
}
public void fitToWindow()
{
zoom=Math.min(((double)viewPort.getWidth())/((double)oriImage.getXDim()), ((double)viewPort.getHeight())/((double)oriImage.getYDim()));
centerToWindow();
}
public void centerToWindow()
{
centerXToWindow();
centerYToWindow();
}
public double viewSizeX(){
return zoom*(double)oriImage.getXDim();
}
public double viewSizeY(){
return zoom*(double)oriImage.getYDim();
}
public void centerXToWindow()
{
int shiftXc=(int)(((double)viewPort.getWidth()-viewSizeX())/2.0);
if(shiftXc!=shiftX)
{
shiftX=shiftXc;
fireChangeEvent();
}
}
public void centerYToWindow()
{
int shiftYc=(int)(((double)viewPort.getHeight()-viewSizeY())/2.0);
if(shiftYc!=shiftY)
{
shiftY=shiftYc;
fireChangeEvent();
}
}
public void noBlanckY()
{
int shiftYc=(int)Math.max(Math.min(0,shiftY),viewPort.getHeight()-viewSizeY());
if(shiftYc!=shiftY)
{
shiftY=shiftYc;
fireChangeEvent();
}
}
public void noBlanckX()
{
int shiftXc=(int)Math.max(Math.min(0,shiftX),viewPort.getWidth()-viewSizeX());
if(shiftXc!=shiftX)
{
shiftX=shiftXc;
fireChangeEvent();
}
}
public int getAbsoluteXCoord(int xMousePosition)
{
return (int)Math.max(Math.min((((double)xMousePosition-(double)shiftX)/zoom),oriImage.xdim-1),0);
}
public int getRelativeXCoord(int xImagePosition)
{
return (int)(((double)xImagePosition)*zoom+(double)shiftX);
}
public int getRelativeYCoord(int yImagePosition)
{
return (int)(((double)yImagePosition)*zoom+(double)shiftY);
}
public int getAbsoluteYCoord(int yMousePosition)
{
return (int)Math.max(Math.min((((double)yMousePosition-(double)shiftY)/zoom),oriImage.ydim-1),0);
}
public double [] getPixelValue(int x, int y)
{
double [] res=null;
x=getAbsoluteXCoord(x);
y=getAbsoluteYCoord(y);
res=oriImage.getVectorPixelXYZTDouble(x, y, 0, 0);
return res;
}
/**
* @return the display
*/
public BufferedImage getDisplay() {
return display;
}
/**
* @return the scaleResult
*/
public boolean isScaleResult() {
return scaleResult;
}
private void clearTempImg()
{
if(myimg != null)
for(int i=0;i<myimg.length;i++)
myimg[i]=null;
}
/**
* @param scaleResult the scaleResult to set
*/
public void setScaleResult(boolean scaleResult) {
if(this.scaleResult != scaleResult)
{
this.scaleResult = scaleResult;
clearTempImg();
setPreProcessing();
fireChangeEvent();
}
}
/**
* @return the followWindow
*/
public boolean isFollowWindow() {
return followWindow;
}
/**
* @param followWindow the followWindow to set
*/
public void setFollowWindow(boolean followWindow) {
this.followWindow = followWindow;
if(followWindow!=this.followWindow)
fireChangeEvent();
}
/**
* @return the autoFitWindow
*/
public boolean isAutoFitWindow() {
return autoFitWindow;
}
/**
* @param autoFitWindow the autoFitWindow to set
*/
public void setAutoFitWindow(boolean autoFitWindow) {
this.autoFitWindow = autoFitWindow;
if(autoFitWindow!=this.autoFitWindow)
fireChangeEvent();
}
/**
* Does not throw change event !!!!
* @param v
*/
public void copyAttribute(View v)
{
zoom=v.zoom;
shiftX=v.shiftX;
shiftY=v.shiftY;
//autoCorrect=v.autoCorrect;
autoFitWindow=v.autoFitWindow;
//scaleResult=v.scaleResult;
followWindow=v.followWindow;
setDisplayedBand(v.displayedBand);
//setAutoCorrectLevel(v.getAutoCorrectLevel());
fireChangeEvent();
}
public ViewPort getViewPort() {
return viewPort;
}
public void setViewPort(ViewPort viewPort) {
this.viewPort = viewPort;
}
public void setActive(boolean active) {
this.active = active;
}
/* ***********************************
* Change event thrower
*/
private ArrayList<ChangeListener> listeners=new ArrayList<ChangeListener>();
public void addChangeListener(ChangeListener cl)
{
listeners.add(cl);
}
public void removeChangeListener(ChangeListener cl)
{
listeners.remove(cl);
}
public boolean isRegistredChangeListener(ChangeListener cl)
{
return listeners.contains(cl);
}
public void fireChangeEvent()
{
ChangeEvent e = new ChangeEvent(this);
for(ChangeListener cl:listeners)
cl.stateChanged(e);
}
/**
* @return the inverseGrayScale
*/
public boolean isInverseGrayScale() {
return inverseGrayScale;
}
/**
* @param inverseGrayScale the inverseGrayScale to set
*/
public void setInverseGrayScale(boolean inverseGrayScale) {
this.inverseGrayScale = inverseGrayScale;
clearTempImg();
setPreProcessing();
}
public boolean isColoured() {
return coloured;
}
private void checkBandsForColour()
{
if(colourBandR <0 || colourBandR >= copyImage.bdim)
colourBandR=0;
if(colourBandG <0 || colourBandG >= copyImage.bdim)
colourBandG=(copyImage.bdim>1)?1:0;
if(colourBandB <0 || colourBandB >= copyImage.bdim)
colourBandB=(copyImage.bdim>2)?2:0;
}
public void setColoured(boolean coloured) {
if(coloured != this.coloured)
{
this.coloured = coloured;
setDisplay();
fireChangeEvent();
}
}
public int getDisplayedBand() {
return displayedBand;
}
public void setDisplayedBand(int displayedBand) {
displayedBand=Math.min(Math.max(0,displayedBand),copyImage.bdim-1);
if(displayedBand != this.displayedBand)
{
this.displayedBand = displayedBand;
setDisplay();
fireChangeEvent();
}
}
public int getColourBandR() {
return colourBandR;
}
public void setColourBandR(int coulourBandR) {
if(this.colourBandR != coulourBandR)
{
this.colourBandR = coulourBandR;
checkBandsForColour();
setDisplay();
fireChangeEvent();
}
}
public int getColourBandG() {
return colourBandG;
}
public void setColourBandG(int coulourBandG) {
if(this.colourBandG != coulourBandG)
{
this.colourBandG = coulourBandG;
checkBandsForColour();
setDisplay();
fireChangeEvent();
}
}
public int getColourBandB() {
return colourBandB;
}
public void setColourBandB(int coulourBandB) {
if(this.colourBandB != coulourBandB)
{
this.colourBandB = coulourBandB;
checkBandsForColour();
setDisplay();
fireChangeEvent();
}
}
public boolean isThreshold() {
return threshold;
}
public void setThreshold(boolean threshold) {
if(threshold != this.threshold)
{
this.threshold = threshold;
clearTempImg();
setPreProcessing();
fireChangeEvent();
}
}
public double getThresholdValue(int band) {
return thresholdValue[band];
}
public void setThresholdValue(int band, double thresholdValue) {
if(thresholdValue != this.thresholdValue[band])
{
this.thresholdValue[band] = thresholdValue;;
clearTempImg();
setPreProcessing();
fireChangeEvent();
}
}
/**
* Convert BufferedImage image to pelican ByteImage
* @param img
* @return
*/
public static Image bufferedImageTopelicanImage(BufferedImage img) {
/*PixelGrabber pixelGrabber = new PixelGrabber(img, 0, 0, 320, 240, true);
pixelGrabber.setDimensions(320, 240);
try {
while (!pixelGrabber.grabPixels())
;
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//int[] data = (int[]) pixelGrabber.getPixels();
int type=img.getType();
int nbbande=3;
if(type == BufferedImage.TYPE_BYTE_BINARY || BufferedImage.TYPE_BYTE_GRAY==type || type == BufferedImage.TYPE_BYTE_INDEXED || type ==BufferedImage.TYPE_USHORT_GRAY )
nbbande=1;
ByteImage imgPiaf = new ByteImage(img.getWidth(), img.getHeight(), 1, 1, nbbande);
Raster r =img.getData();
//int [] data = r.getSamples(0, 0, img.getWidth(), img.getHeight(), 3, (int[])null);
if(nbbande==1)
{
for (int i = 0; i < imgPiaf.getXDim(); i++) {
for (int j = 0; j < imgPiaf.getYDim(); j++) {
imgPiaf.setPixelXYBByte(i, j, 0,r.getSample(i, j, 0));
}
}
}else{
int [] data = img.getRGB(0, 0, img.getWidth(), img.getHeight(), (int[])null , 0, img.getWidth());
for (int i = 0; i < imgPiaf.getXDim(); i++) {
for (int j = 0; j < imgPiaf.getYDim(); j++) {
imgPiaf.setPixelXYBByte(i, j, 0,(data[j* imgPiaf.xdim + i] >> 16) & 0xff);
imgPiaf.setPixelXYBByte(i, j, 1,(data[j* imgPiaf.xdim + i] >> 8) & 0xff);
imgPiaf.setPixelXYBByte(i, j, 2,(data[j* imgPiaf.xdim + i] ) & 0xff);
/* old way
imgPiaf.setPixelXYBByte(i, j, 0, new Color(data[j
* imgPiaf.getXDim() + i]).getRed());
imgPiaf.setPixelXYBByte(i, j, 1, new Color(data[j
* imgPiaf.getXDim() + i]).getGreen());
imgPiaf.setPixelXYBByte(i, j, 2, new Color(data[j
* imgPiaf.getXDim() + i]).getBlue());
* */
}
}
}
return imgPiaf;
}
/**
* @return the multiBandPolicyCorrection
*/
public MultiBandPolicy getMultiBandPolicyCorrection() {
return multiBandPolicyCorrection;
}
/**
* @param multiBandPolicyCorrection the multiBandPolicyCorrection to set
*/
public void setMultiBandPolicyCorrection(
MultiBandPolicy multiBandPolicyCorrection) {
if(multiBandPolicyCorrection != this.multiBandPolicyCorrection)
{
this.multiBandPolicyCorrection = multiBandPolicyCorrection;
clearTempImg();
setPreProcessing();
fireChangeEvent();
}
}
/**
* @return the gammaModel
*/
public GammaModel getGammaModel() {
return gammaModel;
}
/**
* @param gammaModel the gammaModel to set
*/
public void setGammaModel(GammaModel gammaModel) {
if(gammaModel!=this.gammaModel)
{
this.gammaModel = gammaModel;
switch (gammaModel)
{
case No:
gammaCompressionModel=null;
break;
case Simple:
gammaCompressionModel=new SimpleGammaCompressionModel();
break;
case sRGB:
gammaCompressionModel=new SRGBGammaCompressionModel();
break;
case REC709:
gammaCompressionModel=new REC709GammaCompressionModel();
break;
}
clearTempImg();
setPreProcessing();
fireChangeEvent();
}
}
}