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

boofcv.gui.feature.CompareTwoImagePanel Maven / Gradle / Ivy

Go to download

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

The newest version!
/*
 * Copyright (c) 2011-2016, 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.feature;

import georegression.geometry.UtilPoint2D_F64;
import georegression.struct.point.Point2D_F64;
import georegression.struct.point.Point2D_I32;
import org.ddogleg.struct.GrowQueue_I32;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;


/**
 * Panel for displaying two images next to each other separated by a border.
 *
 * @author Peter Abeles
 */
public abstract class CompareTwoImagePanel extends JPanel implements MouseListener , MouseMotionListener{

	// how close a click needs to be to a point
	private double clickDistance = 20;

	// list of features in both images
	protected List leftPts,rightPts;

	// draw a selected pair
	List selected = new ArrayList<>();
	protected boolean selectedIsLeft;

	// can it select more than one?
	protected boolean selectRegion;
	
	// size of the border between the images
	protected int borderSize;

	// left and right window information
	protected BufferedImage leftImage,rightImage;
	protected double scaleLeft,scaleRight;

	// where it first clicked when selecting a region
	protected Point2D_I32 firstClick;
	// current position of the mouse while being dragged
	protected Point2D_I32 mousePosition = new Point2D_I32();
	
	public CompareTwoImagePanel(int borderSize , boolean canSelectRegion) {
		this.borderSize = borderSize;
		this.selectRegion = canSelectRegion;
		addMouseListener(this);
		addMouseMotionListener(this);
	}

	public void setLocation( List leftPts , List rightPts) {
		this.leftPts = leftPts;
		this.rightPts = rightPts;
		selected.clear();
	}

	/**
	 * Sets the internal images.  Not thread safe.
	 *
	 * @param leftImage
	 * @param rightImage
	 */
	public synchronized void setImages(BufferedImage leftImage , BufferedImage rightImage ) {
		this.leftImage = leftImage;
		this.rightImage = rightImage;

		int width = leftImage.getWidth() + rightImage.getWidth()+borderSize;
		int height = Math.max(leftImage.getHeight(),rightImage.getHeight());
		setPreferredSize(new Dimension(width,height));
	}


	@Override
	public synchronized void paintComponent(Graphics g) {
		super.paintComponent(g);
		if( leftImage == null || rightImage == null )
			return;

		computeScales();

		Graphics2D g2 = (Graphics2D)g;

		// location in the current frame, taking in account the scale of each image
		int x1 = (int)(scaleLeft*leftImage.getWidth());
		int x2 = x1+borderSize;
		int x3 = x2+(int)(scaleRight*rightImage.getWidth());
		int y1 = (int)(scaleLeft*leftImage.getHeight());
		int y2 = (int)(scaleRight*rightImage.getHeight());

		// draw the background images
		g2.drawImage(leftImage,0,0,x1,y1,0,0,leftImage.getWidth(),leftImage.getHeight(),null);
		g2.drawImage(rightImage,x2,0,x3,y2,0,0,rightImage.getWidth(),rightImage.getHeight(),null);

		drawFeatures(g2,scaleLeft,0,0,scaleRight,x2,0);
		
		// draw the selected region
		if( selectRegion && firstClick != null ) {
			int x0 = mousePosition.getX() < firstClick.x ? mousePosition.getX() : firstClick.x;
			x1 = mousePosition.getX() >= firstClick.x ? mousePosition.getX() : firstClick.x;
			int y0 = mousePosition.getY() < firstClick.y ? mousePosition.getY() : firstClick.y;
			y1 = mousePosition.getY() >= firstClick.y ? mousePosition.getY() : firstClick.y;

			g2.setColor(Color.WHITE);
			g2.setStroke(new BasicStroke(3));
			g2.drawRect(x0,y0,x1-x0,y1-y0);
			g2.setColor(Color.BLACK);
			g2.setStroke(new BasicStroke(1));
			g2.drawRect(x0,y0,x1-x0,y1-y0);
		}
	}

	/**
	 * Implement this function to draw features related to each image.
	 *
	 * @param scaleLeft Scale of left image.
	 * @param leftX Left image (0,0) coordinate.
	 * @param leftY Left image (0,0) coordinate.
	 * @param scaleRight Scale of right image.
	 * @param rightX Right image (0,0) coordinate.
	 * @param rightY Right image (0,0) coordinate.
	 */
	protected abstract void drawFeatures( Graphics2D g2 ,
										  double scaleLeft , int leftX , int leftY ,
										  double scaleRight , int rightX , int rightY );

	/**
	 * Compute individually how each image will be scaled
	 */
	private void computeScales() {
		int width = getWidth();
		int height = getHeight();

		width = (width-borderSize)/2;

		// compute the scale factor for each image
		scaleLeft = scaleRight = 1;
		if( leftImage.getWidth() > width || leftImage.getHeight() > height ) {
			double scaleX = (double)width/(double)leftImage.getWidth();
			double scaleY = (double)height/(double)leftImage.getHeight();
			scaleLeft = Math.min(scaleX,scaleY);
		}
		if( rightImage.getWidth() > width || rightImage.getHeight() > height ) {
			double scaleX = (double)width/(double)rightImage.getWidth();
			double scaleY = (double)height/(double)rightImage.getHeight();
			scaleRight = Math.min(scaleX,scaleY);
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		firstClick = null;
		selected.clear();
		if( e.getClickCount() > 1 ) {
			repaint();
			return;
		}

		int leftEndX = (int)(scaleLeft*leftImage.getWidth());
		int rightBeginX = leftEndX + borderSize;

		if( e.getX() < leftEndX ) {
			selectedIsLeft = true;

			int x = (int)(e.getX()/scaleLeft);
			int y = (int)(e.getY()/scaleLeft);

			findBestPoints(x, y, leftPts , selected );

		} else if( e.getX() >= rightBeginX ) {
			selectedIsLeft = false;

			int x = (int)((e.getX()-rightBeginX)/scaleRight);
			int y = (int)(e.getY()/scaleRight);

			findBestPoints(x, y, rightPts , selected );
		}
//		System.out.println("selected index "+selectedIndex);
		repaint();
	}

	private void findBestPoints(int x, int y,  List pts , List selected ) {
		double bestDist = clickDistance*clickDistance;
		GrowQueue_I32 bestIndexes = new GrowQueue_I32(20);
		for( int i = 0; i < pts.size(); i++ ) {
			if( !isValidPoint(i) )
				continue;

			Point2D_F64 p = pts.get(i);
			double d = UtilPoint2D_F64.distanceSq(p.x, p.y, x, y);
			if( d < bestDist ) {
				bestDist = d;
				bestIndexes.reset();
				bestIndexes.add(i);
			} else if( Math.abs(d - bestDist) < 0.01 ) {
				bestIndexes.add(i);
			}
		}

		if( bestIndexes.size() > 0 ) {
			int indexRight = bestIndexes.get(0);
		}

		for (int i = 0; i < bestIndexes.size(); i++) {
			selected.add(bestIndexes.get(i));
		}
	}

	protected abstract boolean isValidPoint( int index );

	@Override
	public void mousePressed(MouseEvent e) {
		if( selectRegion )
			firstClick = new Point2D_I32(e.getX(),e.getY());
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		if( !selectRegion ) {
			return;
		}

		// adjust the selected region for scale and the image that was selected
		int leftEndX = (int)(scaleLeft*leftImage.getWidth());
		int rightBeginX = leftEndX + borderSize;
		selectedIsLeft = e.getX() < leftEndX;

		int x0 = e.getX() < firstClick.x ? e.getX() : firstClick.x;
		int x1 = e.getX() >= firstClick.x ? e.getX() : firstClick.x;
		int y0 = e.getY() < firstClick.y ? e.getY() : firstClick.y;
		int y1 = e.getY() >= firstClick.y ? e.getY() : firstClick.y;

		double scale = selectedIsLeft ? scaleLeft : scaleRight;
		
		if( selectedIsLeft) {
			x0 /= scale;
			x1 /= scale;
		} else {
			x0 = (int)((x0 - rightBeginX)/scale);
			x1 = (int)((x1 - rightBeginX)/scale);
		}
		y0 /= scale;
		y1 /= scale;
		
		// find all the points in the region
		if( selectedIsLeft ) {
			findPointsInRegion(x0,y0,x1,y1,leftPts);
		} else {
			findPointsInRegion(x0,y0,x1,y1,rightPts);
		}
		
		// reset the selector
		firstClick = null;

		repaint();
	}
	
	private void findPointsInRegion( int x0 , int y0 , int x1 , int y1 , List pts )
	{
		selected.clear();
		for( int i = 0; i < pts.size(); i++ ) {
			if( !isValidPoint(i) )
				continue;

			Point2D_F64 p = pts.get(i);
			
			if( p.x >= x0 && p.x < x1 && p.y >= y0 && p.y < y1 ) {
				selected.add(i);
			}
		}
	}

	@Override
	public void mouseEntered(MouseEvent e) {}

	@Override
	public void mouseExited(MouseEvent e) {}

	@Override
	public void mouseDragged(MouseEvent e){
		if( selectRegion ) {
			mousePosition.x = e.getX();
			mousePosition.y = e.getY();
			repaint();
		}
	}

	@Override
	public void mouseMoved(MouseEvent e){}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy