Search code examples
swingopencvjavafxjpanelpane

JPanel.getGraphics equivalent in JavaFx to use with OpenCV


The title seems a little bit confusing, but I'll explain everything.

I'm developing a project where I show the image captured by a webcam in a JPanel, Java Swing. Now I have to integrate this with JavaFx. I have a controller where I have the method startRecording, that would initialize the cameraThread and tell the class Camera to startRecording, inside Camera class a have a method DrawFrame(BufferedImage, JPanel panel) where I call the function drawImage from OpenCV to draw in the Panel:

Controller:

public void startRecording(){
    cameraInstance.setCameraRGBPanel(windowsInstance.getCameraRGBPanel());
    cameraInstance.setCameraHSVPanel(windowsInstance.getCameraHSVPanel());
    cameraInstance.setCameraThresholdPanel(windowsInstance.getCameraThresholdPanel());
    cameraInstance.setRecord(true);
    cameraThread = new Thread(cameraInstance);
    cameraThread.start();
}

Class camera:

private void drawFrame(BufferedImage buff, JPanel pane){
    pane.getGraphics().drawImage(buff, 0, 0, null);
}

To start with, JavaFX has no JPanel and the Pane (an option) has no getGraphics, I've tried to use a SwingNode, add the JPanel and then do everything as usual, but the image simply won't be shown.

The following code was a test, that's why it seems to be so 'bad'.

public void start(Stage stage) throws Exception {

    stage.setTitle("Tela Teste");

    pCamera = new Pane();
    SwingNode swing = new SwingNode();
    pCamera.getChildren().add(swing);
    createAndSetSeingContent(swing);

    Group root = new Group();
    root.getChildren().add(pCamera);
    stage.setScene(new Scene(root , 500, 500));
    stage.setResizable(true);
    stage.show();
}

private void createAndSetSeingContent(SwingNode swing) {                
    ControllerCamera control = new ControllerCamera();
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

    JPanel panel = new JPanel();
    JLabel label = new JLabel("Abc");
    //panel.add(label);
    swing.setContent(panel);

    Button teste = new Button("A");
    pCamera.getChildren().add(teste);
    teste.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            control.startRecording(panel);
            System.out.println("abc");
        }
    });
}

I changed to method startRecording to something like:

public void startRecording(JPanel panel){
    cameraInstance.setCameraRGBPanel(panel);
    cameraInstance.setRecord(true);
    cameraThread = new Thread(cameraInstance);
    cameraThread.start();
}

Still nothing appears in the panel, but if I add a label or button, then it appear and works as intended to. The "abc" is always shown in the console.

I think that's all the code related to the problem. Something else I want to say is that yesterday was the first day I was dealing with FX, let's say the project is divided, the other guy is also working on the problem, but we haven't gotten anywhere so far, that's why I decided to ask you here.

Edit 1: everything was working perfectly before all this situation (everything works with Swing, but not in FX).


Solution

  • The simplest way to display an Image in JavaFX is with an ImageView. You can create a single ImageView and update its image by calling setImage(...), passing in a javafx.scene.image.Image. I don't know the camera API you are working with: you might be able to generate a JavaFX image directly, in which case your draw frame method looks as simple as:

    private void drawFrame(Image image, ImageView imageView) {
        imageView.setImage(image);
    }
    

    If you can only generate BufferedImages, you can do

    private void drawFrame(BufferedImage buff, ImageView imageView) {
        imageView.setImage(SwingFXUtils.toFXImage(buff, null));
    }
    

    In either case, you can just create the ImageView, put it in a Pane subclass of some kind, put the Pane in a scene and display it in the Stage:

    public void start(Stage stage) throws Exception {
    
        stage.setTitle("Tela Teste");
    
        pCamera = new Pane();
        ImageView imageView = new ImageView();
        pCamera.getChildren().add(imageView);
    
        Group root = new Group();
        root.getChildren().add(pCamera);
        stage.setScene(new Scene(root , 500, 500));
        stage.setResizable(true);
        stage.show();
    }
    

    Then just pass the imageView to your drawFrame method as needed.