/* * 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<Point2D_F64> leftPts,rightPts; // draw a selected pair List<Integer> 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<Point2D_F64> leftPts , List<Point2D_F64> 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<Point2D_F64> pts , List<Integer> 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<Point2D_F64> 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){} }