有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Javafx图像根据鼠标悬停位置进行裁剪

我正在开发一个追踪程序,将点放在X射线的解剖点上。我的问题是,我试图放大我的X光图像(Imageview),而我的鼠标悬停在它上面,并在另一个Imageview中显示。我希望我的鼠标在其周围创建一个虚拟矩形,并在移动时保持在中心位置,并将该实时图像传输到其他小Imageview,到目前为止,我已经这样做了,但存在一些小问题。我的长方形跟不上我的鼠标,当我移动到角落时,故障会加速,你有什么建议?我正在分享我的错误和代码,以澄清所有这些事情

也许我应该创建一个矩形,并将其放置在鼠标位置周围,实时裁剪图像,放入zoomedImageview中,并一直显示,但我被困在了这一点上

主要问题是,鼠标位置和zoomedImageview的中心位置必须始终重合

enter image description here

X射线图像视图的宽度和高度为800x800 ZoomMedimageView Fitwidth高度为250x250

public class DrawingController {

        @FXML 
    ImageView xrayImage;
        
         @FXML
    ImageView zoomedImage;

   public void initialize()
    {

   xrayImage.setOnMouseMoved(e -> {    //HOVERING ON XRAY
   
PixelReader reader = XrayImageview.getImage().getPixelReader();   
WritableImage newImage = new WritableImage(reader,

(int) e.getX(),  //x and y is picked according to hovering mouse position
   (int) e.getY(),
   (int) 250,     // 250x250 rectangle is requested.
   (int) 250);

zoomedImage.setImage(newImage);  // Displaying zoomed image

        });
}}

我的FXML文件是:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="913.0" prefWidth="1238.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.java.controllers.DrawingController">
   <children>
      <ImageView fitHeight="800.0" fitWidth="800.0" layoutX="418.0" layoutY="33.0" pickOnBounds="true" preserveRatio="true" fx:id="xrayImage">
         <image>
            <Image url="@threatCare.png" />
         </image>
      </ImageView>
      <ImageView fx:id="zoomedImage" fitHeight="250.0" fitWidth="250.0" layoutX="108.0" layoutY="433.0" pickOnBounds="true" preserveRatio="true" />
   </children>
</AnchorPane>

主要课程是:

package main.java;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import main.java.database.DatabaseCreator;

import javax.swing.text.html.ImageView;
import java.awt.*;

public class Runner extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception{
     
        Parent root = FXMLLoader.load(getClass().getResource("../resources/DrawingPanel.fxml"));

        primaryStage.setTitle("TreatCare Software Systems");
         Scene scene = new Scene(root,1200,950);
        primaryStage.setScene(scene);
        primaryStage.setMinWidth(1200);
        primaryStage.setMinHeight(950);
        primaryStage.setMaximized(true);
        primaryStage.setOnCloseRequest( e -> SystemClose());
        primaryStage.show();
    }

    private void SystemClose(){
        System.exit(0);         
    }

    public static void main(String[] args){
        launch(args);
    }
}

共 (2) 个答案

  1. # 1 楼答案

    我在^{中添加了一个窗格。这个Pane的目的是显示被种植区域的边界(一个标记):

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.image.Image?>
    <?import javafx.scene.image.ImageView?>
    <?import javafx.scene.layout.AnchorPane?>
    <?import javafx.scene.layout.Pane?>
    <?import javafx.scene.layout.StackPane?>
    
    <AnchorPane prefHeight="913.0" prefWidth="1238.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" 
                                                                                fx:controller="DrawingController">
       <children>
          <StackPane layoutX="418.0" layoutY="33.0">
             <children>
                <ImageView  fitHeight="800.0" fitWidth="800.0" preserveRatio="true" pickOnBounds="true" fx:id="xrayImagePane">
                   <image>         
                    <Image url="https://www.almanac.com/sites/default/files/image_nodes/cosmos-flower.jpg" />
                   </image>
                </ImageView>
                <!  Stack a Pane over the ImageView. Add the marker to this Pane  >
                <Pane fx:id="markerPane" prefHeight="200.0" prefWidth="200.0" />
             </children>
          </StackPane>
          <ImageView fx:id="croppedImageView" fitHeight="250.0" fitWidth="250.0" layoutX="108.0" layoutY="433.0" pickOnBounds="true" preserveRatio="true" />
       </children>
    </AnchorPane>
    

    控制器添加和更新标记,并更新裁剪后的图像。我从VGR's answer中借用了一些代码来处理这个比例,但请注意使用getLayoutBounds()来计算这个比例:

    import javafx.fxml.FXML;
    import javafx.geometry.Bounds;
    import javafx.scene.image.*;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    
    public class DrawingController {
    
        @FXML
        ImageView xrayImagePane;
        private Image fullImage;
        private PixelReader reader;
    
        @FXML
        Pane markerPane;
        private Rectangle marker;
    
        @FXML
        ImageView croppedImageView;
    
        private static int ZOOM_WONDOW_SIZE = 250;
        private static Color MARKER_COLOR  = Color.BLUE;
    
        @FXML
        public void initialize(){
    
            marker = new Rectangle(0,0, ZOOM_WONDOW_SIZE, ZOOM_WONDOW_SIZE);
            marker.setStroke(MARKER_COLOR);
            marker.setFill(Color.TRANSPARENT);
            marker.setStrokeWidth(3);
            markerPane.getChildren().add(marker);
    
            fullImage = xrayImagePane.getImage();
            reader = fullImage.getPixelReader();
    
            markerPane.setOnMouseMoved(e -> {
                updateMarker(e.getX(), e.getY());
                updateZoomImage();
            });
        }
    
        //update marker location based on mouse location
        private void updateMarker(double x, double y) {
            //use layout bounds because you do not know if the actual size is xrayImagePane.getFitWidth()
            //or fullImage.getWidth()
            Bounds imageViewBounds = xrayImagePane.getLayoutBounds();
            double imageWidth = imageViewBounds.getWidth();
            double newX= Math.max(0, x - ZOOM_WONDOW_SIZE/2 );
            newX =  newX + ZOOM_WONDOW_SIZE >= imageWidth ?  imageWidth - ZOOM_WONDOW_SIZE : newX;
    
            double imageHeight = imageViewBounds.getHeight();
            double newY = Math.max(0, y - ZOOM_WONDOW_SIZE/2 );
            newY=  newY + ZOOM_WONDOW_SIZE >= imageHeight ?  imageHeight - ZOOM_WONDOW_SIZE : newY;
    
            marker.setX(newX);
            marker.setY(newY);
        }
    
        //update zoomed image based on marker and scale
        private void updateZoomImage() {
    
            //adapted from VGR's answer https://stackoverflow.com/a/69273511/3992939
            double x = marker.getX();
            double y = marker.getY();
            double width = marker.getWidth();
            double height= marker.getHeight();
            //use layout bounds because you do not know if the actual size is xrayImagePane.getFitWidth()
            //or fullImage.getWidth()
            Bounds imageViewBounds = xrayImagePane.getLayoutBounds();
    
            double xScale = xrayImagePane.getFitWidth() > 0 ?
                                fullImage.getWidth() / imageViewBounds.getWidth() : 1;
            double yScale = xrayImagePane.getFitHeight() > 0 ?
                               fullImage.getHeight() / imageViewBounds.getHeight(): 1;
            if (xrayImagePane.isPreserveRatio()) {
                double scale = Math.min(xScale, yScale);
                x *= scale;
                y *= scale;
                width *= scale;
                height *= scale;
            } else {
                x *= xScale;
                y *= yScale;
                width *= xScale;
                height *= yScale;
            }
    
            WritableImage croppedImage = new WritableImage(reader,
                    (int) x,
                    (int) y,
                    (int) width,
                    (int) height
                    );
            croppedImageView.setImage(croppedImage); 
        }
    }
    

    使用

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.*;
    import javafx.stage.Stage;
    
    public class Runner extends Application {
        @Override
        public void start(Stage primaryStage) throws Exception{
    
            Parent root = FXMLLoader.load(getClass().getResource("DrawingPanel.fxml"));
    
            primaryStage.setTitle("TreatCare Software Systems");
             Scene scene = new Scene(root);
            primaryStage.setScene(scene);
    
            primaryStage.setOnCloseRequest( e -> SystemClose());
            primaryStage.show();
        }
    
        private void SystemClose(){
            System.exit(0);
        }
    
        public static void main(String[] args){
            launch(args);
        }
    }
    

    enter image description here

  2. # 2 楼答案

    您正在缩放图像,因此需要根据该缩放转换鼠标坐标

    从你的。fxml文件:

    <ImageView fitHeight="800.0" fitWidth="800.0" layoutX="418.0" layoutY="33.0" pickOnBounds="true" preserveRatio="true" fx:id="xrayImage">
    

    该行中有三个属性用于缩放图像:

    • fitWidth="800.0"将图像缩放到800像素宽
    • fitHeight="800.0"将图像缩放到800像素高
    • preserveRatio="true"-覆盖所有缩放,因此图像在两个维度上始终具有相同的缩放比例

    所以你需要根据以下公式乘以鼠标坐标:

    xrayImage.setOnMouseMoved(e -> {    //HOVERING ON XRAY
        Image image = xrayImage.getImage();
    
        double x = e.getX();
        double y = e.getY();
        double xScale = (xrayImage.getFitWidth() > 0 ?
            image.getWidth() / xrayImage.getFitWidth() : 1);
        double yScale = (xrayImage.getFitHeight() > 0 ?
            image.getHeight() / xrayImage.getFitHeight() : 1);
        if (xrayImage.isPreserveRatio()) {
            double scale = Math.max(xScale, yScale);
            x *= scale;
            y *= scale;
        } else {
            x *= xScale;
            y *= yScale;
        }
    
        x = Math.max(0, x - 125);
        y = Math.max(0, y - 125);
        double width = Math.min(image.getWidth() - x, 250);
        double height = Math.min(image.getHeight() - y, 250);
    
        PixelReader reader = image.getPixelReader();   
        WritableImage newImage = new WritableImage(reader,
            (int) x, (int) y, (int) width, (int) height);
    
        zoomedImage.setImage(newImage);  // Displaying zoomed image
    });