/*
 * @(#) ImageProcessor.java 1.0 2000/10/4
 * Copyright (c) 2000 Larry Rodrigues
 */
package app;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.color.*;
import java.awt.geom.*;
import com.sun.image.codec.jpeg.*;
import java.beans.*;
import javax.media.jai.*;
import java.awt.image.renderable.*;
import com.vistech.jai.render.*;
import java.util.*;
//import com.vistech.jai.roi.*;
import com.vistech.jai.util.*;
import com.vistech.util.*;

 /** An appliction with image processing examples.
   * @version 1.0  10 April 2000
   * @author Lawrence Rodrigues
   */
public class ImageProcessor extends JPanel {
   JAIImageCanvas sourceCanvas, destCanvas;
   int imageWidth, imageHeight;
   int viewerWidth, viewerHeight;
   PlanarImage sourceImage;
   JAIWindowLevel windowLevel;
   WindowLevelGUI wlpan;

   public PlanarImage readAsPlanarImage(String filename) {
       RenderedOp im =  JAI.create("fileload", filename);
       return reformatImage(im, new Dimension(256,256));
   }

   public void loadAndDisplay(String filename){
      sourceImage = readAsPlanarImage(filename);
      displayImage(sourceImage);
   }

   public void displayImage(PlanarImage img) {
      int width = img.getWidth();
      int height = img.getHeight();
      displayImage(img, width, height);
   }


   private Dimension getViewerSize(double imageAspectRatio){
      Dimension d= Toolkit.getDefaultToolkit().getScreenSize();
      int maxWidth = (int)(d.width *4/5.0);
      int maxHeight = (int)(d.height*4/5.0);
      double screenAspectRatio = maxWidth/(double)maxHeight;
      int viewerWidth,viewerHeight;
      if(imageAspectRatio  > screenAspectRatio){
          viewerWidth =   maxWidth;
          viewerHeight = (int)( maxHeight* screenAspectRatio/imageAspectRatio);
      }else{
          viewerHeight =   maxHeight;
          viewerWidth = (int)(maxWidth* imageAspectRatio/screenAspectRatio);
      }
      return new Dimension(viewerWidth, viewerHeight);
   }

   protected RenderedOp reformatImage(PlanarImage img, Dimension tileDim) {
      int tileWidth = tileDim.width;
      int tileHeight = tileDim.height;
      ImageLayout tileLayout = new ImageLayout(img);

      tileLayout.setTileWidth(tileWidth);
      tileLayout.setTileHeight(tileHeight);
      HashMap map = new HashMap();
      map.put(JAI.KEY_IMAGE_LAYOUT, tileLayout);
      map.put(JAI.KEY_INTERPOLATION,
             Interpolation.getInstance(Interpolation.INTERP_BICUBIC));
      RenderingHints tileHints = new RenderingHints(map);
      ParameterBlock pb = new ParameterBlock();
      pb.addSource(img);
      return JAI.create("format", pb, tileHints);
   }

   public void displayImage(PlanarImage img, int width, int height) {
      JFrame fr = new JFrame();
      fr.addWindowListener(
         new WindowAdapter() {
            public void windowClosing(WindowEvent e){
               System.exit(0);
            }
         } );
      fr.setTitle("Image Processor");
      imageWidth = img.getWidth();
      imageHeight = img.getHeight();
      double aspectRatio = imageWidth/(double)imageHeight;
      Dimension d = getViewerSize(aspectRatio);
      viewerWidth = d.width;
      viewerHeight = d.height;
      width = d.width/2;
      height = d.width/2;
      sourceCanvas= new JAIImageCanvas(img);
      sourceCanvas.setPreferredSize(new Dimension(imageWidth, imageHeight));
      JScrollPane srcjsp = new JScrollPane(sourceCanvas);
      srcjsp.setBorder(BorderFactory.createTitledBorder("Source Canvas"));

      destCanvas = new JAIImageCanvas(img);
      destCanvas.setPreferredSize(new Dimension(imageWidth, imageHeight));
      JScrollPane destjsp = new JScrollPane(destCanvas);
      destjsp.setBorder(BorderFactory.createTitledBorder("Destination Canvas"));

      JPanel viewerpan = new JPanel();
      viewerpan.setSize(2*width, height);
      viewerpan.setLayout(new GridLayout(1,2));
      viewerpan.add(srcjsp);
      viewerpan.add(destjsp);
      Container cp = fr.getContentPane();
      //fr.getContentPane().setLayout(new BorderLayout());
      JTabbedPane jtp = new JTabbedPane();
      EnhancePanel enhancepan = new EnhancePanel();
      jtp.add("Enhance", enhancepan);
      EdgeDetectPanel  edgedetectpan = new EdgeDetectPanel();
      jtp.add("Edge Detect", edgedetectpan);
      ColorConvertPanel colorconvertpan = new ColorConvertPanel();
      jtp.add("Color Convert", colorconvertpan);
      BandCombinePanel  bandcombinepan = new BandCombinePanel();
      jtp.add("Band Combine", bandcombinepan);
      BandSelectPanel  bspan = new BandSelectPanel();
      jtp.add("Band Select", bspan);
      windowLevel = new JAIWindowLevel( img, destCanvas);
      wlpan = new WindowLevelGUI(windowLevel);
      setWLSliderValues();
      jtp.add("Window Level", wlpan);
      FrequencyOpPanel frop = new FrequencyOpPanel();
      //jtp.add("Frequency Op", frop);


      JSplitPane vsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, viewerpan,jtp);
      cp.add(vsp);
      //show();
      fr.pack();
      fr.setSize(new Dimension(2*width+20, height+130));
      fr.show();
      vsp.setDividerLocation(0.7);
      vsp.setDividerSize(6);
      sourceCanvas.repaint();
      destCanvas.repaint();
      addEvents();
   }

   protected void addEvents() {
      destCanvas.addPropertyChangeListener(
         new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt){
               String propName = evt.getPropertyName();
               if(propName == "image") {
                  setWLSliderValues();
               }
            }
        }
      );
   }

   public void setWLSliderValues(){
       PlanarImage image = destCanvas.getImage();
       double[][] minmax = getMinMax(image, null);
       //System.out.println("first = "+ minmax.length);
       //System.out.println("second = "+ minmax[0].length);
       wlpan.initSliders(minmax);
  }

   public static double[][] getMinMax(PlanarImage image, ROI roi) {
      ParameterBlock pb = new ParameterBlock();
      pb.addSource(image);
      pb.add(roi);
      pb.add(1);
      pb.add(1);
      RenderedOp op = JAI.create("extrema", pb);
      double[][] minmax = (double[][]) op.getProperty("extrema");
      return minmax;
   }
  
   public void reset(){
     // if(sourceCanvas!= null) sourceCanvas.resetImage();
     // if(destCanvas != null) destCanvas.resetImage();
   }

   protected void displayOrig() {
      PlanarImage srcImage= sourceCanvas.getImage();
      destCanvas.setImage(srcImage);
      destCanvas.repaint();
   }

   public static RenderedOp sharpen(PlanarImage image) {
      float sharpMatrix[] = {-1.0f, -1.0f, -1.0f,
                             -1.0f, 9.0f, -1.0f,
                             -1.0f, -1.0f, -1.0f};
      KernelJAI  sharpKernel = new KernelJAI (3,3, sharpMatrix);
      return JAI.create("convolve", image, sharpKernel);
   }

   public static RenderedOp  blur(PlanarImage image) {
      float blurmatrix[] = {1/16f, 1/8f, 1/16f,
                          1/8f, 1/4f, 1/8f,
                          1/16f, 1/8f, 1/16f};
      KernelJAI  blurKernel = new KernelJAI (3,3,blurmatrix);
      return JAI.create("convolve", image, blurKernel);
   }

   public static RenderedOp sobelHoriz(PlanarImage image) {
      KernelJAI sobelHorizKernel = KernelJAI.GRADIENT_MASK_SOBEL_HORIZONTAL;
      return JAI.create("convolve", image, sobelHorizKernel);
   }

   public static RenderedOp sobelVert(PlanarImage image) {
      KernelJAI sobelVertKernel = KernelJAI.GRADIENT_MASK_SOBEL_VERTICAL;
      return JAI.create("convolve", image, sobelVertKernel);
  }

   public static RenderedOp sobelGradientMagnitude(PlanarImage image) {
      KernelJAI sobelVertKernel = KernelJAI.GRADIENT_MASK_SOBEL_VERTICAL;
      KernelJAI sobelHorizKernel = KernelJAI.GRADIENT_MASK_SOBEL_HORIZONTAL;
      ParameterBlock pb = new ParameterBlock();
      pb.addSource(image);
      pb.add(sobelHorizKernel);
      pb.add(sobelVertKernel);
      return JAI.create("gradientmagnitude", pb);
   }

   public void displayDestImage(PlanarImage op){
      destCanvas.setImage(op);
      destCanvas.repaint();
   }

   public static RenderedOp grayColorConvert(PlanarImage image, ColorSpace cs) {
 
      //JAI 1.1 change. Parameter changed to ColorModel
      //return JAI.create("ColorConvert", image,ColorSpace.getInstance(ColorSpace.CS_GRAY) );
      RenderedOp op = null;
      try {
         ColorModel cm = image.getColorModel();
         //String str = ImageInfoUtil.getColorModelAsText(ImageInfoUtil.getColorModelType(cm));
                 
         //System.out.println("col model = "+ str);
         int numBands = image.getData().getNumBands();
         int[] bits = new int[numBands];
         for(int i=0;i<numBands;i++) {
             bits[i] = 8;
         }
         ComponentColorModel ccm = new ComponentColorModel(
                          cs,
                          bits,
                          cm.hasAlpha(), 
                          cm.isAlphaPremultiplied(),
                          cm.getTransparency(),
                          cm.getTransferType());
         if(!ccm.isCompatibleSampleModel(image.getSampleModel())){
            System.out.println("Can't be converted. Incomptible sample model");
            op = null;
         }else  op = JAI.create("ColorConvert", image,ccm );

      } catch (Exception e) {
        System.out.println("Can't be converted. Can't create compatible color model");
        op = null;
      }finally {
         return op;
      }
   }

   public static RenderedOp grayBandCombine(PlanarImage image) {
      double[][] grayBandCombineMatrix = {{0.212671f, 0.715160f,0.071169f, 0.0f}};
      return JAI.create("BandCombine",image, grayBandCombineMatrix);
   }

   public static RenderedOp bandSelect(PlanarImage image, int bandNum) {
      int numbands = image.getData().getNumBands();
      if(bandNum >= numbands) return null;
      return JAI.create("BandSelect",image, new int[] {bandNum});
   }

   class ColorConvertPanel extends JPanel{
      public ColorConvertPanel(){
         createUI();
      }
      protected void createUI() {
         ButtonGroup bg = new ButtonGroup();
         JRadioButton orig = new JRadioButton("Original");
         orig.setSelected(true);
         orig.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayOrig();
             }
            }
         );
         JRadioButton graybc= new JRadioButton("Gray scale");
         graybc.setSelected(false);
         graybc.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
                 PlanarImage img = grayColorConvert(sourceImage,cs);
                 if(img == null) return;
                 displayDestImage(img);
              }
            }
         );

         JRadioButton lrgb= new JRadioButton("Linear RGB");
         lrgb.setSelected(false);
         lrgb.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
                 PlanarImage img = grayColorConvert(sourceImage,cs);
                 if(img == null) return;
                 displayDestImage(img);
              }
            }
         );

         JRadioButton srgb= new JRadioButton("sRGB");
         srgb.setSelected(false);
         srgb.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
                 PlanarImage img = grayColorConvert(sourceImage,cs);
                 if(img == null) return;
                 displayDestImage(img);
              }
            }
         );


         JRadioButton cie= new JRadioButton("CIEXYZ");
         cie.setSelected(false);
         cie.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ);
                 PlanarImage img = grayColorConvert(sourceImage,cs);
                 if(img == null) return;
                 displayDestImage(img);
              }
            }
         );

         bg.add(orig);
         bg.add(graybc);
         bg.add(lrgb);
         bg.add(srgb);
         bg.add(cie);
 

         add(orig);
         add(graybc);
         add(lrgb);
         add(srgb);
         add(cie);
      }
   }

   class BandCombinePanel extends JPanel{
      public BandCombinePanel(){
         createUI();
      }
      protected void createUI() {
         ButtonGroup bg = new ButtonGroup();
         JRadioButton orig = new JRadioButton("Original");
         orig.setSelected(true);
         orig.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                displayOrig();
              }
            }
         );
         JRadioButton graybc= new JRadioButton("Gray scale");
         graybc.setSelected(false);
         graybc.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayDestImage(grayBandCombine(sourceImage));
              }
            }
         );
         bg.add(orig);
         bg.add(graybc);
         add(orig);
         add(graybc);
      }
   }

   class BandSelectPanel extends JPanel{
      public BandSelectPanel(){
         createUI();
      }
      protected void createUI() {
         ButtonGroup bg = new ButtonGroup();
         JButton orig = new JButton("Original");
         orig.addActionListener(
            new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                displayOrig();
              }
            }
         );
         JPanel pan = new JPanel(new GridLayout(2,2));
         JLabel lab2 = new JLabel("Select a band");
         JTextField txtf = new JTextField(3);
         int numb =0;
         if(sourceImage != null) numb = sourceImage.getSampleModel().getNumBands();
         JLabel lab1 = new JLabel("Number of bands = "+ numb);
         pan.add(lab2);
         pan.add(txtf);
         JPanel pan1 = new JPanel(new BorderLayout());
         pan1.add(BorderLayout.NORTH, lab1);
         pan1.add(BorderLayout.WEST, lab2);
         pan1.add(BorderLayout.CENTER, txtf);
         pan1.add(BorderLayout.SOUTH, orig);
         txtf.addActionListener(
            new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                 try {
                   JTextField tf = (JTextField)e.getSource();
                   String txt = tf.getText();
                   int num = Integer.parseInt(txt);
                   int numbands = sourceImage.getSampleModel().getNumBands();
                   if(num >= numbands) num = numbands-1;
                   displayDestImage(bandSelect(sourceImage, num));
                 } catch(Exception e1) {e1.printStackTrace();}
              }
            }
         );
         add(pan1);
      }
   }


   class EdgeDetectPanel extends JPanel{
      public EdgeDetectPanel(){
         createUI();
      }
      protected void createUI() {
         JRadioButton orig = new JRadioButton("Original");
         orig.setSelected(true);
         orig.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayOrig();
              }
            }
         );
         ButtonGroup bg = new ButtonGroup();
         JRadioButton sobelv = new JRadioButton("Sobel Vert");
         sobelv.setSelected(false);
         sobelv.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayDestImage(sobelVert(sourceImage));
              }
            }
         );
         JRadioButton sobelh = new JRadioButton("Sobel Horiz");
         sobelh.setSelected(false);
         sobelh.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayDestImage(sobelHoriz(sourceImage));
              }
            }
        );
        JRadioButton sobelgm = new JRadioButton("Sobel Gradient Magnitude");
         sobelgm.setSelected(false);
         sobelgm.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayDestImage(sobelGradientMagnitude(sourceImage));
              }
            }
        );
        bg.add(orig);
        bg.add(sobelv);
        bg.add(sobelh);
        bg.add(sobelgm);
        setLayout(new GridLayout(2,2));
        add(orig);
        add(sobelv);
        add(sobelh);
        add(sobelgm);
      }
   }

   class EnhancePanel extends JPanel{
      public EnhancePanel(){
         createUI();
      }
      protected void createUI() {
         ButtonGroup bg = new ButtonGroup();
         JRadioButton orig = new JRadioButton("Original");
         orig.setSelected(true);
         orig.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayOrig();
              }
            }
         );
         JRadioButton sharp = new JRadioButton("Sharpen");
         sharp.setSelected(false);
         sharp.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayDestImage(sharpen(sourceImage));
              }
            }
         );

         JRadioButton blur = new JRadioButton("Blur");
         blur.setSelected(false);
         blur.addChangeListener(
            new ChangeListener() {
              public void stateChanged(ChangeEvent e) {
                 displayDestImage(blur(sourceImage));
              }
            }
         );
         bg.add(orig);
         bg.add(sharp);
         bg.add(blur);
         add(orig);
         add(sharp);
         add(blur);
      }
   }

   class FrequencyOpPanel extends JPanel /* implements  TabPanel */
   {
   public static final int DFT = 1;
   public static final int DCT =2;
   public static final int MAG = 4;
   public static final int MAG_SQ = 8;
   public static final int PHASE = 16;

   protected ButtonGroup bg;
   protected int currentMode = DFT;

   public FrequencyOpPanel(){ createUI();}
   public void openPanel() {
      applyOperator(currentMode);
   }
   public void closePanel(){ }

   protected void createUI() {
      bg = new ButtonGroup();
      JRadioButton dft = new JRadioButton("DFT");
      JRadioButton dct = new JRadioButton("DCT");
      JRadioButton mag = new JRadioButton("Magnitude");
      JRadioButton magsq = new JRadioButton("Magnitide Squared");
      JRadioButton phase = new JRadioButton("Phase");

      FequencyOpAdapter dfta = new FequencyOpAdapter(DFT);
      FequencyOpAdapter  dcta= new FequencyOpAdapter(DCT);
      FequencyOpAdapter maga = new FequencyOpAdapter(MAG);
      FequencyOpAdapter magsqa= new FequencyOpAdapter(MAG_SQ);
      FequencyOpAdapter  phasea= new FequencyOpAdapter(PHASE);

      mag.setSelected(true);

      dft.addActionListener(dfta);
      dct.addActionListener(dcta);
      mag.addActionListener(maga);
      magsq.addActionListener(magsqa);
      phase.addActionListener(phasea);

      bg.add(dft);
      bg.add(mag);
      bg.add(magsq);
      bg.add(phase);
      bg.add(dct);

      setLayout(new GridLayout(2,2));
      add(dft);
      add(mag);
      add(magsq);
      add(phase);
      add(dct);
   }
   protected void applyOperator(int mode) {
      if (sourceImage == null)  return;
      RenderedOp op = null, op1=null;
      switch(mode) {
         case DFT:
             op = JAIOpUtil.DFTImage(sourceImage,
                                     javax.media.jai.operator.DFTDescriptor.SCALING_NONE,
                                     javax.media.jai.operator.DFTDescriptor.REAL_TO_COMPLEX);
             break;
         case DCT:
             op = JAIOpUtil.DCTImage(sourceImage);
             break;
         case MAG:
             op1 = JAIOpUtil.DFTImage(sourceImage,
                                     javax.media.jai.operator.DFTDescriptor.SCALING_NONE,
                                     javax.media.jai.operator.DFTDescriptor.REAL_TO_COMPLEX);
             op = JAIOpUtil.magnitudeImage(op1);
             break;
         case MAG_SQ:
             op1 = JAIOpUtil.DFTImage(sourceImage,
                                     javax.media.jai.operator.DFTDescriptor.SCALING_NONE,
                                     javax.media.jai.operator.DFTDescriptor.REAL_TO_COMPLEX);
             op = JAIOpUtil.magnitudeSquaredImage(op1);
             break;
         case PHASE:
             op1 = JAIOpUtil.DFTImage(sourceImage,
                                     javax.media.jai.operator.DFTDescriptor.SCALING_NONE,
                                     javax.media.jai.operator.DFTDescriptor.REAL_TO_COMPLEX);
             op = JAIOpUtil.phaseImage(op1);
             break;
       }
       if(op == null) return;
       //createNewLayout(UNARY_FORMAT);
       PlanarImage destImage = op.createInstance();
       destCanvas.setImage(destImage);
   }
   class FequencyOpAdapter implements ActionListener {
      int mode;
      public FequencyOpAdapter( int type) {mode = type;}
      public void actionPerformed(ActionEvent e) {
         currentMode = mode;
         applyOperator(mode);
     }
   }
}

   public static void  main(String[] args){
      ImageProcessor ip = new ImageProcessor();
      if(args.length <1) {
         System.out.println("Enter a valid image file name");
         System.exit(0);
      }
      ip.loadAndDisplay(args[0]);
   }
 }
