/* This program 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. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. N.B. the above text was copied from http://www.gnu.org/licenses/gpl.html unmodified. I have not attached a copy of the GNU license to the source... Copyright (C) 2011 Timo Rantalainen, tjrantal@gmail.com */ /*Program for analysing activity indices from vertical GRFs collected from underneath animal housing. Only useful for University of Jyvaskyla, who actually have such measurement apparatus. The WDQ file reading may be of use to DataQ or old Codas users. */ package ui; import javax.swing.*; //GUI komennot swing import java.awt.event.*; //Eventit ja Actionlistener import java.io.*; //File IO import java.lang.Math; import java.awt.*; import java.awt.geom.Line2D; import javax.swing.event.*; import javax.swing.border.*; import java.util.Vector; import java.util.Enumeration; import java.awt.font.*; import java.text.*; import analysis.*; //FrameByFrame import ui.*; //AnalysisThread import DrawImage.*; //AnalysisThread import dlt.*; //Direct linear transformation import Jama.*; //Jama Matrix import utils.*; //XCorrelation import java.net.URL; /*implements AL antaa mahdollisuuden kayttaa eventtteja koneelta. Kayttis toteuttaa...*/ /*extends = inherit, voi peria vain yhden*/ public class JavaVideoAnalysis extends JPanel implements ActionListener, ChangeListener, MouseListener,WindowListener { public JButton videoToOpen; public JButton fileToOpen; public JButton calibrationFile; public JButton openFile; public JButton closeFile; public JButton trackMarker; public JTextField lowPass; public JLabel status; public JPanel sliderPane; public JSlider slider; public File selectedFile; public File videoFile; public String savePath; public String initPath; public JFrame videoFrame; //Frame for video //public JFrame calibrationFrame; public SaveableFrame pointFrame; //Frame for digitized points public JTextArea pointTextArea; public SaveableFrame calibrationFrame; //Frame for digitized points public JTextArea calibrationTextArea; public Vector<String[]> calibrations; public int pointsDigitized; public int[][] digitizedCalibration; public AnalysisThread analysisThread; public int[] currentVideoFrame = null; public DrawImage drawImage; public DLT2D dlt2d = null; public int width; public int height; public int[] lastCoordinates = null; public DigitizedPoints digitizedPoints = null; private Thread anaThread; public boolean autotrack; public JavaVideoAnalysis(){ videoFile = null; savePath = null; pointsDigitized = 0; digitizedCalibration = null; autotrack = false; /*Preset path*/ String videoSourceString =new String(""); String videoSavePath = new String(""); File vali = new File("user.dir"); boolean current = true; //true = using current, false = preset path if (!current){ vali = new File("/home/rande/programming/javaVideo/winTesti"); } /*CURRENT PATH*/ initPath = new String(); if (current){ initPath = System.getProperty("user.dir"); }else{ initPath = vali.getAbsolutePath(); } /*Add buttons and textfield...*/ JPanel buttons = new JPanel(); buttons.setLayout(new GridLayout(5,2,5,5)); /*Set button layout...*/ videoToOpen= new JButton("Video file to Open"); videoToOpen.setMnemonic(KeyEvent.VK_C); videoToOpen.setActionCommand("videoFile"); videoToOpen.addActionListener(this); videoToOpen.setToolTipText("Press to select file."); buttons.add(new JLabel(new String("Video file to use"))); buttons.add(videoToOpen); /* openFile = new JButton("JavaVideoAnalysis"); openFile.setMnemonic(KeyEvent.VK_I); openFile.setActionCommand("openFile"); openFile.addActionListener(this); openFile.setToolTipText("Press to Open file."); buttons.add(new JLabel(new String("Click to Open File"))); buttons.add(openFile); */ calibrationFile= new JButton("CalibrationFile"); calibrationFile.setMnemonic(KeyEvent.VK_R); calibrationFile.setActionCommand("calibrate"); calibrationFile.addActionListener(this); calibrationFile.setToolTipText("Press to select calibration object file."); buttons.add(new JLabel(new String("Select calibration object"))); buttons.add(calibrationFile); trackMarker= new JButton("AutoTrack"); trackMarker.setMnemonic(KeyEvent.VK_T); trackMarker.setActionCommand("autoTrack"); trackMarker.addActionListener(this); trackMarker.setToolTipText("Press to autoTrack marker."); trackMarker.setEnabled(false); //Disable for now buttons.add(new JLabel(new String("Autotrack Marker"))); buttons.add(trackMarker); closeFile = new JButton("CloseVideo"); closeFile.setMnemonic(KeyEvent.VK_S); closeFile.setActionCommand("closeFile"); closeFile.addActionListener(this); closeFile.setToolTipText("Press to Close file."); buttons.add(new JLabel(new String("Click to Close File"))); buttons.add(closeFile); status = new JLabel(new String("Ready to Rumble")); buttons.add(status); add(buttons); } public static void initAndShowGU(){ JFrame frame = new JFrame("JavaVideoAnalysis"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JComponent newContentPane = new JavaVideoAnalysis(); newContentPane.setOpaque(true); //content panes must be opaque frame.setContentPane(newContentPane); frame.pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int w = 300; int h = 200; frame.setLocation(20, 20); //f.setLocation(screenSize.width/2 - w/2, screenSize.height/2 - h/2); //f.setSize(w, h); frame.setVisible(true); } /*ChangeListener*/ public void stateChanged(ChangeEvent e) { if (analysisThread != null){ if (analysisThread.frameByFrame != null){ JSlider source = (JSlider)e.getSource(); if (!source.getValueIsAdjusting()) { int targetFrame = (int)source.getValue(); System.out.println("Target Frame "+targetFrame); currentVideoFrame = analysisThread.frameByFrame.readFrame(targetFrame); } } } } /*WindowListener*/ public void windowActivated(WindowEvent e){ //System.out.println("Activated"); } public void windowClosed(WindowEvent e){ //System.out.println("Closed"); } /*If window is closed, close associated classes*/ public void windowClosing(WindowEvent e){ //System.out.println("Closing"); if (analysisThread != null){ if (analysisThread.frameByFrame != null){ closeFrameByFame(); } } } public void windowDeactivated(WindowEvent e){} public void windowDeiconified(WindowEvent e){} public void windowIconified(WindowEvent e){} public void windowOpened(WindowEvent e){} /*MouseListener*/ public void mouseClicked(MouseEvent me) { } public void mousePressed(MouseEvent me) { } public void mouseExited(MouseEvent me) { } public void mouseEntered(MouseEvent me) { } public void mouseReleased(MouseEvent me) { if (analysisThread != null){ if (analysisThread.frameByFrame != null){ if (me.getButton() == MouseEvent.BUTTON1){ if (lastCoordinates == null){ lastCoordinates = new int[2]; } lastCoordinates[0] = me.getX(); lastCoordinates[1] = me.getY(); trackMarker.setEnabled(true); if (digitizedCalibration != null){ //Next clicks after initializing calibration will be calibration System.out.println("adding calibrated point"); for (int i = 0;i<2;++i){ digitizedCalibration[pointsDigitized][i] = lastCoordinates[i]; calibrationFrame.data.get(pointsDigitized+1).add(Integer.toString(lastCoordinates[i])); } ++pointsDigitized; calibrationFrame.writeCalibrationFile(); if (pointsDigitized <calibrationFrame.data.size()-1){ status.setText(new String("Digitize "+calibrationFrame.data.get(pointsDigitized+1).get(0))); }else{ calculateCoefficients(); } /*Draw x and y -coordinate?*/ }else{ if (dlt2d == null){ System.out.println("screen(X,Y) = " + lastCoordinates[0] + "," + lastCoordinates[1]); } else { double[] digitizedPoint = {(double) lastCoordinates[0], (double) lastCoordinates[1]}; Matrix coordinates = dlt2d.scaleCoordinates(digitizedPoint); double[][] scaledPoint = coordinates.getArray(); System.out.println("screen(X,Y) = " + lastCoordinates[0] + "," + lastCoordinates[1] +" calibrated = "+coordinates.get(0,0) +","+coordinates.get(1,0)); //Add the digitizedPoints.addPoint(digitizedPoint,scaledPoint, currentVideoFrame[0]); pointFrame.writeDigitizedPoints(digitizedPoints); //Go to next frame currentVideoFrame = analysisThread.frameByFrame.readFrame(); } } } } } } public void calculateCoefficients(){ /*Get the DLT coefficients and store calibration in dlt2d*/ dlt2d = null; //Get rid of existing calibration double[][] global = new double[calibrationFrame.data.size()-1][2]; //Global coordinates of the calibration object double[][] digitizedPoints = new double[calibrationFrame.data.size()-1][2]; /*Copy the calibration object*/ for (int r = 0; r< calibrationFrame.data.size()-1;++r){ for (int c = 0; c<2;++c){ System.out.println(calibrationFrame.data.get(r+1).get(c+1)); global[r][c] = Double.parseDouble(calibrationFrame.data.get(r+1).get(c+1)); } } /*Copy the calibration points*/ for (int r = 0; r< digitizedPoints.length;++r){ for (int c = 0; c< digitizedPoints[r].length;++c){ digitizedPoints[r][c] = (double) digitizedCalibration[r][c]; } } digitizedCalibration = null; dlt2d = new DLT2D(global,digitizedPoints); /*Print coefficients...*/ Matrix coeffs = dlt2d.getCurrentDltCoefficients(); String resultString = "Coefficients\n"; for (int i = 0; i< coeffs.getRowDimension();++i){ resultString += "\tCoeff "+i+": "+Double.toString(coeffs.get(i,0))+"\n"; } System.out.println(resultString); status.setText(new String("2D DLT coefficients")); /*Create text pane for coordinates*/ pointFrame = new SaveableFrame(this); pointFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); pointFrame.pack(); pointFrame.setLocation(10, 600); pointFrame.setVisible(true); } /*ActionListener*/ public void actionPerformed(ActionEvent e) { if ("videoFile".equals(e.getActionCommand())){ JFileChooser chooser = new JFileChooser(initPath); //chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int returnVal = chooser.showOpenDialog(this); if(returnVal == JFileChooser.APPROVE_OPTION) { videoFile = chooser.getSelectedFile(); System.out.println("Video file "+videoFile.getName()); initPath = videoFile.getAbsolutePath(); status.setText(new String("videoFileChosen")); } videoToOpen.setEnabled(false); //openFile.setEnabled(false); if (videoFile == null){ videoFile =new File("H:/UserData/winMigrationBU/Deakin/JGREYADJUST/CMJ.avi"); } if (savePath == null){ savePath = new String("H:/UserData/winMigrationBU/Deakin/JGREYADJUST/figs/"); } System.out.println("Open file "+videoFile.getName()); System.out.println("Save path "+savePath); try{ currentVideoFrame = new int[1]; currentVideoFrame[0] = 0; analysisThread = new AnalysisThread(this); anaThread = new Thread(analysisThread,"analysisThread"); anaThread.start(); //All of the analysis needs to be done within this thread from hereafter //anaThread.join(); }catch (Exception err){System.out.println("Failed analysis thread"+err);} } if ("calibrate".equals(e.getActionCommand())){ calibrationFrame = new SaveableFrame(this); calibrationFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); calibrationFrame.pack(); calibrationFrame.setLocation(600, 600); calibrationFrame.setVisible(true); calibrationFrame.callLoad(); } /*Try to autotrack marker*/ if ("autoTrack".equals(e.getActionCommand())){ autotrack = true; trackMarker.setEnabled(false); /*Get template to track*/ double[][] template = new double[21][21]; int width = analysisThread.frameByFrame.videoSize.width; int height = analysisThread.frameByFrame.videoSize.height; for (int h = 0;h<21;++h){ for (int w = 0;w<21;++w){ template[w][h] = (double) analysisThread.frameByFrame.frameData[(w-10+lastCoordinates[0])+(h-10+lastCoordinates[1])*width]; } } /*Autotrack until match is not found or run out of video*/ while (autotrack && currentVideoFrame[0] < analysisThread.frameByFrame.frameCount){ /*cross correlate with image*/ double[][] sliceData = new double[width][height]; for (int h = 0;h<height;++h){ for (int w = 0;w<width;++w){ sliceData[w][h] = (double) analysisThread.frameByFrame.frameData[w+h*width]; } } double[][] xcorr= Filters.xcorr(sliceData,template); /*Set areas not of interest to -10*/ for (int i = 0; i<xcorr.length; ++i){ for (int j = 0; j<xcorr[i].length; ++j){ if (i < lastCoordinates[0]-10 || i > lastCoordinates[0]+10 || j < lastCoordinates[1]-10 || j > lastCoordinates[1]+10){ xcorr[i][j] = -1; } } } /*Best guess*/ Max max = Filters.getMax(xcorr); double[] digitizedPoint = {(double) max.indices[0], (double) max.indices[1]}; Matrix coordinates = dlt2d.scaleCoordinates(digitizedPoint); double[][] scaledPoint = coordinates.getArray(); System.out.println("screen(X,Y) = " + lastCoordinates[0] + "," + lastCoordinates[1] +" calibrated = "+coordinates.get(0,0) +","+coordinates.get(1,0)); //Add the digitizedPoints.addPoint(digitizedPoint,scaledPoint, currentVideoFrame[0]); pointFrame.writeDigitizedPoints(digitizedPoints); //Go to next frame currentVideoFrame = analysisThread.frameByFrame.readFrame(); } } if ("openFile".equals(e.getActionCommand())) { videoToOpen.setEnabled(false); //openFile.setEnabled(false); if (videoFile == null){ videoFile =new File("H:/UserData/winMigrationBU/Deakin/JGREYADJUST/CMJ.avi"); } if (savePath == null){ savePath = new String("H:/UserData/winMigrationBU/Deakin/JGREYADJUST/figs/"); } System.out.println("Open file "+videoFile.getName()); System.out.println("Save path "+savePath); try{ currentVideoFrame = new int[1]; currentVideoFrame[0] = 0; analysisThread = new AnalysisThread(this); anaThread = new Thread(analysisThread,"analysisThread"); anaThread.start(); //All of the analysis needs to be done within this thread from hereafter //anaThread.join(); }catch (Exception err){System.out.println("Failed analysis thread"+err);} //System.gc(); //Try to enforce carbage collection } //closeFile if ("closeFile".equals(e.getActionCommand())) { WindowEvent wev = new WindowEvent(this.videoFrame, WindowEvent.WINDOW_CLOSING); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); /*Close the digitized points window as well*/ wev = new WindowEvent(this.pointFrame, WindowEvent.WINDOW_CLOSING); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); /*Close the calibration window also*/ wev = new WindowEvent(this.calibrationFrame, WindowEvent.WINDOW_CLOSING); Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); } } private void closeFrameByFame(){ videoToOpen.setEnabled(true); //openFile.setEnabled(true); try{ analysisThread.frameByFrame.close(); analysisThread = null; anaThread.join(); anaThread = null; currentVideoFrame = null; digitizedPoints = null; dlt2d = null; System.gc(); //Try to enforce carbage collection }catch (Exception err){System.out.println("Failed analysis thread"+err);} } public static void main(String[] args){ javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run(){ initAndShowGU(); } } ); } }