All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.gui.SelectAlgorithmAndInputPanel Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

There is a newer version: 1.1.7
Show newest version
/*
 * Copyright (c) 2021, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.gui;

import boofcv.gui.image.ImagePanel;
import boofcv.io.MediaManager;
import boofcv.io.PathLabel;
import boofcv.io.wrapper.DefaultMediaManager;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Provides pull a menubar for selecting the input source and which algorithm to use
 *
 * @author Peter Abeles
 */
@SuppressWarnings({"unchecked", "NullAway.Init"})
public abstract class SelectAlgorithmAndInputPanel extends JPanel
		implements ActionListener, VisualizeApp {
	JToolBar toolbar;
	// each combo box is used to select different algorithms
	JComboBox[] algBoxes;
	// used to select the input image
	JComboBox imageBox;
	// when selected it shows the original image
	protected JCheckBox originalCheck;
	List[] algCookies;
	// list of input names and where to get the inputs
	protected List inputRefs;
	protected String baseDirectory = "";

	// components which had been externally added
	List addedComponents = new ArrayList<>();

	// what the original image was before any processing
	protected BufferedImage inputImage;
	// panel used for displaying the original image
	ImagePanel origPanel = new ImagePanel();
	// the main GUI being displayed
	Component gui;
	// should it post algorithm change events yet?
	boolean postAlgorithmEvents = false;

	// abstract way of reading in media
	protected MediaManager media = DefaultMediaManager.INSTANCE;

	protected SelectAlgorithmAndInputPanel( int numAlgFamilies ) {
		super(new BorderLayout());
		toolbar = new JToolBar();

		imageBox = new JComboBox<>();
		toolbar.add(imageBox);
		imageBox.addActionListener(this);
		imageBox.setMaximumSize(imageBox.getPreferredSize());

		algBoxes = new JComboBox[numAlgFamilies];
		algCookies = new List[numAlgFamilies];
		for (int i = 0; i < numAlgFamilies; i++) {
			var b = algBoxes[i] = new JComboBox<>();
			toolbar.add(b);
			b.addActionListener(this);
			b.setMaximumSize(b.getPreferredSize());
			algCookies[i] = new ArrayList<>();
		}

		toolbar.add(Box.createHorizontalGlue());

		originalCheck = new JCheckBox("Show Input");
		toolbar.add(originalCheck);
		originalCheck.addActionListener(this);

		originalCheck.setEnabled(false);

		add(toolbar, BorderLayout.PAGE_START);
	}

	/**
	 * Loads a standardized file for input references
	 *
	 * @param fileName path to config file
	 */
	@Override
	public void loadInputData( String fileName ) {
		Reader r = media.openFile(fileName);

		List refs = new ArrayList<>();
		try {
			BufferedReader reader = new BufferedReader(r);

			String line;
			while ((line = reader.readLine()) != null) {

				String[] z = line.split(":");
				String[] names = new String[z.length - 1];
				for (int i = 1; i < z.length; i++) {
					names[i - 1] = baseDirectory + z[i];
				}

				refs.add(new PathLabel(z[0], names));
			}

			setInputList(refs);
		} catch (IOException e) {
			throw new UncheckedIOException(e);
		}
	}

	/**
	 * Sets the directory that relative references are relative too
	 */
	public void setBaseDirectory( String baseDirectory ) {
		this.baseDirectory = baseDirectory;
	}

	/**
	 * Adds a new component into the toolbar.
	 *
	 * @param comp The component being added
	 */
	public void addToToolbar( JComponent comp ) {
		toolbar.add(comp, 1 + algBoxes.length);
		toolbar.revalidate();
		addedComponents.add(comp);
	}

	public void removeFromToolbar( JComponent comp ) {
		toolbar.remove(comp);
		toolbar.revalidate();
		addedComponents.remove(comp);
	}

	/**
	 * Used to add the main GUI to this panel. Must use this function.
	 * Algorithm change events will not be posted until this function has been set.
	 *
	 * @param gui The main GUI being displayed.
	 */
	public void setMainGUI( final Component gui ) {
		postAlgorithmEvents = true;
		this.gui = gui;
		SwingUtilities.invokeLater(() -> add(gui, BorderLayout.CENTER));
	}

	/**
	 * Specifies an image which contains the original input image. After this has been called the
	 * view input image widget is activated and when selected this image will be displayed instead
	 * of the main GUI. This functionality is optional.
	 *
	 * @param image Original input image.
	 */
	public void setInputImage( BufferedImage image ) {
		inputImage = image;
		SwingUtilities.invokeLater(() -> {
			if (inputImage == null) {
				originalCheck.setEnabled(false);
			} else {
				originalCheck.setEnabled(true);
				origPanel.setImage(inputImage);
				origPanel.setPreferredSize(new Dimension(inputImage.getWidth(), inputImage.getHeight()));
				origPanel.repaint();
			}
		});
	}

	/**
	 * Specifies a list of images to use as input and loads them
	 *
	 * @param inputRefs Name of input and where to get it
	 */
	public void setInputList( final List inputRefs ) {
		this.inputRefs = inputRefs;

		SwingUtilities.invokeLater(() -> {
			for (int i = 0; i < inputRefs.size(); i++) {
				imageBox.addItem(inputRefs.get(i).getLabel());
			}
		});
	}

	public void addAlgorithm( final int indexFamily, final String name, Object cookie ) {
		algCookies[indexFamily].add(cookie);
		SwingUtilities.invokeLater(() -> algBoxes[indexFamily].addItem(name));
	}

	/**
	 * Grabs the currently selected algorithm, passes information to GUI for updating, toggles GUI
	 * being active/not. refreshAll() is called in a new thread.
	 */
	public void doRefreshAll() {
		SwingUtilities.invokeLater(() -> {
			// collect the current state inside the GUI thread
			final Object[] state = new Object[algCookies.length];
			for (int i = 0; i < state.length; i++) {
				state[i] = algCookies[i].get(algBoxes[i].getSelectedIndex());
			}
			// create a new thread to process this change
			new Thread(() -> {
				setActiveGUI(false);
				refreshAll(state);
				setActiveGUI(true);
			}).start();
		});
	}

	/**
	 * Enables/disables the ability to interact with the algorithms GUI.
	 */
	private void setActiveGUI( final boolean isEnabled ) {
		SwingUtilities.invokeLater(() -> {
			toolbar.setEnabled(isEnabled);
			for (JComboBox b : algBoxes) {
				b.setEnabled(isEnabled);
			}
			for (JComponent b : addedComponents) {
				b.setEnabled(isEnabled);
			}
			imageBox.setEnabled(isEnabled);
		});
	}

	/**
	 * Returns the cookie associated with the specified algorithm family.
	 */
	protected  T getAlgorithmCookie( int indexFamily ) {
		return (T)algCookies[indexFamily].get(algBoxes[indexFamily].getSelectedIndex());
	}

	@Override
	public void actionPerformed( ActionEvent e ) {
		for (int i = 0; i < algBoxes.length; i++) {
			if (algBoxes[i] == e.getSource()) {
				// see if its ready to start posting these events
				if (!postAlgorithmEvents)
					return;

				// notify the main GUI to change the input algorithm
				final Object cookie = algCookies[i].get(algBoxes[i].getSelectedIndex());
				final String name = (String)algBoxes[i].getSelectedItem();
				final int indexFamily = i;

				new Thread(() -> performSetAlgorithm(indexFamily, name, cookie)).start();
				return;
			}
		}

		if (e.getSource() == imageBox) {
			// notify the main GUI to change the input image
			final String name = (String)imageBox.getSelectedItem();
			new Thread(() -> performChangeInput(name, imageBox.getSelectedIndex())).start();
		} else if (e.getSource() == originalCheck) {
			origPanel.setSize(gui.getWidth(), gui.getHeight());
			// swap the main GUI with a picture of the original input image
			if (originalCheck.isSelected()) {
				remove(gui);
				add(origPanel);
			} else {
				remove(origPanel);
				add(gui);
			}
			validate();
			repaint();
		}
	}

	private void performSetAlgorithm( int indexFamily, String name, Object cookie ) {
		setActiveGUI(false);
		setActiveAlgorithm(indexFamily, name, cookie);
		setActiveGUI(true);
	}

	private void performChangeInput( String name, int index ) {
		setActiveGUI(false);
		changeInput(name, index);
		setActiveGUI(true);
	}

	@Override
	public void setMediaManager( MediaManager manager ) {
		this.media = manager;
	}

	/**
	 * Provides the current state of all selected algorithms.
	 *
	 * @param cookies state of each selected algorithm.
	 */
	public abstract void refreshAll( Object[] cookies );

	/**
	 * A request has been made to change the processing algorithm. NOT called from a GUI thread.
	 *
	 * @param name Display name of the algorithm.
	 * @param cookie Reference to user defined data.
	 */
	public abstract void setActiveAlgorithm( int indexFamily, String name, Object cookie );

	/**
	 * A request to change the input image has been made. The input image's label and its index in the
	 * manager are returned.
	 *
	 * @param name Display name of the image.
	 * @param index Which image in the list.
	 */
	public abstract void changeInput( String name, int index );
}