Problem with resizing a canvas with FXGraphics2D and StreamingRenderer - java

I'm in the same situation as here : https://sourceforge.net/p/geotools/mailman/message/35977998/
I am working on a Maps Application using geotools (WMS + WFS for grids) and resizing my JavaFX Canvas works well when I am reducing the size of the canvas, but a part of the image is not rendered when I expend my window (the canvas is expended too).
Is there a solution ?
I'm posting the same example as the one in the link above :
import java.awt.Color;
import java.awt.Rectangle;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.jfree.fx.FXGraphics2D;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ResizingTest extends Application {
#Override
public void start(Stage stage) {
Canvas canvas = new Canvas(640, 480);
BorderPane root = new BorderPane(canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
// Create bindings for resizing.
canvas.widthProperty().bind(root.widthProperty());
canvas.heightProperty().bind(root.heightProperty());
SimpleFeatureTypeBuilder lineFeatureTypeBuilder = new
SimpleFeatureTypeBuilder();
lineFeatureTypeBuilder.setName("LineFeatureType");
lineFeatureTypeBuilder.setCRS(DefaultGeographicCRS.WGS84);
lineFeatureTypeBuilder.add("the_geom", LineString.class,
DefaultGeographicCRS.WGS84);
SimpleFeatureType lineFeatureType = lineFeatureTypeBuilder.buildFe
atureType();
SimpleFeatureBuilder lineFeatureBuilder = new
SimpleFeatureBuilder(lineFeatureType);
DefaultFeatureCollection lines = new DefaultFeatureCollection();
Coordinate[][] cs = {
{ new Coordinate(-1, 42), new Coordinate(4, 46) },
{ new Coordinate(-1, 46), new Coordinate(4, 42) }
};
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFa
ctory();
for(Coordinate [] c : cs) {
LineString line = geometryFactory.createLineString(c);
lineFeatureBuilder.add(line);
SimpleFeature feature = lineFeatureBuilder.buildFeature(null);
lines.add(feature);
}
MapContent map = new MapContent();
Style style = SLD.createLineStyle(Color.RED, 1);
Layer layer = new FeatureLayer(lines, style);
map.addLayer(layer);
//map.getViewport().setBounds(new ReferencedEnvelope(-1, 4, 42, 46,
DefaultGeographicCRS.WGS84));
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
FXGraphics2D graphics = new FXGraphics2D(g);
graphics.setBackground(java.awt.Color.BLUE);
Rectangle rectangle = new Rectangle( (int)
canvas.getWidth(), (int) canvas.getHeight());
graphics.clearRect(0, 0, (int) rectangle.getWidth(), (int)
rectangle.getHeight());
graphics.drawRect(100, 100, 100, 100);
map.getViewport().setScreenArea(rectangle); // Necessary ?
StreamingRenderer renderer = new StreamingRenderer();
renderer.setMapContent(map);
renderer.paint(graphics, rectangle,
map.getViewport().getBounds());
System.out.println("ScreenArea: " +
map.getViewport().getScreenArea() + " - Viewport: " +
map.getViewport().getBounds());
}
};
loop.start();
}
public static void main(String[] args) {
launch(args);
}
}
When we expand the window, a part of the canvas is not rendered anymore, resulting on a "correct" cross (because it still goes from upper left to lower right corner), but cropped ! Anything drawn on the cropped part is not rendered
Edit :
I am not trying to make the canvas resizable, it already is (Proof : The cross goes from upper left to lower right pixel of canvas). The real issue is the rendering of the map that is cropped (we can't see the full cross).

Resizing a canvas can be tricky in JavaFX. Here is a SO answer which may be helpful for you.
Automatically resize Canvas to fill the enclosing Parent

I finally found a fix !
As mentionned in geotools' StreamingRenderer code :
"the way this thing is built is a mess if you try to use it in a
multithreaded environment"
Avoid using StreamingRenderer in an AnimationLoop, Platform.runLater(), etc...
I called the draw function everytime the map was updated instead, and it works as expected ! :)

Related

How to properly implement the Action Listener in a bullseye animation

This program first displays a bullseye created by three different sized circles.
Once the animate me button is clicked, the function animation() will make the existing circles shrink inwards until the size of the circles is zero.
Once the user presses the button named "Press to stop", the animation will then stop. If the user presses the button again, it will then keep going from the state it was stopped from, so on so forth.
Currently, this is not working as intended. It only creates about 9 circles (including the nine circles that the program began with). I know I will need to use the action listener in order to make the program run, but I'm having a hard time in terms of the documentation of the action listener. What am I supposed to put in the parameters of the listener? If you see any other ways around this, please feel free to let me know.
package target;
import javafx.animation.ScaleTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Target extends Application
{
Circle[] cir = new Circle[7];
Button btn = new Button("Animate me!");
StackPane root = new StackPane();
public static void main(String[] args)
{
launch(args);
}
/**
* start method will create the target and the start button first
* displayed on-screen to the user
*/
#Override
public void start(Stage primaryStage)
{
root.setStyle("-fx-border-color:black;");
cir[0] = new Circle(400, 250, 200);
cir[0].setFill(Color.RED);
cir[0].setStyle("-fx-border-color:black;");
cir[1] = new Circle(315, 165, 115);
cir[1].setFill(Color.WHITE);
cir[1].setStyle("-fx-border-color:black;");
cir[2] = new Circle(230, 80, 30);
cir[2].setFill(Color.RED);
cir[2].setStyle("-fx-border-color:black;");
root.getChildren().addAll(cir[0], cir[1], cir[2]);
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
btn.setOnAction(e ->
{
animation();
btn.setText("Press to Stop");
});
}
public void animation()
{
//Timeline animation = new Timeline(
//)
ScaleTransition[] st = new ScaleTransition[7];
boolean recycleCircles = false;
st[0]= new ScaleTransition(Duration.seconds(7), cir[0]);
st[0].setToX(0.0f);
st[0].setToY(0.0f);
st[0].play();
st[1] = new ScaleTransition(Duration.seconds(5.5), cir[1]);
st[1].setToX(0.0f);
st[1].setToY(0.0f);
st[1].play();
st[2] = new ScaleTransition(Duration.seconds(4), cir[2]);
st[2].setToX(0.0f);
st[2].setToY(0.0f);
st[2].play();
// int delayInc = 1;
int delay = 1;
//will create circles (will rotate between white and red) and then add
//to scaleTransitions
//while(btn.isPressed() == false)
{
for(int i = 3; i<st.length; i++)
{
if(recycleCircles == true)
{
i = 0;
recycleCircles = false;
}
if(i % 2 == 1)
{
cir[i] = new Circle(400,250,200);
cir[i].setFill(Color.WHITE);
cir[i].setStyle("-fx-border-color:black;");
root.getChildren().add(cir[i]);
cir[i].toBack();
st[i] = new ScaleTransition(Duration.seconds(7), cir[i]);
st[i].setDelay(Duration.seconds(delay));
delay++;
st[i].setToX(0.0f);
st[i].setToY(0.0f);
st[i].play();
}
else if(i%2==0)
{
cir[i] = new Circle(400, 250, 200);
cir[i].setFill(Color.RED);
cir[i].setStyle("-fx-border-color:black;");
root.getChildren().add(cir[i]);
cir[i].toBack();
st[i] = new ScaleTransition(Duration.seconds(7), cir[i]);
st[i].setDelay(Duration.seconds(delay));
delay++;
st[i].setToX(0.0f);
st[i].setToY(0.0f);
st[i].play();
}
if(i == 6)
recycleCircles = true;
}
}
//btn.pressedProperty().addListener(listener);
btn.setOnMousePressed(event ->
{
});
btn.setOnMouseReleased(event ->
{
for(int y = 0; y<st.length;y++)
{
}
});
}
}
Not sure whether you have any specific use case with each circle. If your are using the circles only for the purpose of alternating row colors, then you can get similar effect with radial gradient's repeat option.
To the extent I understand the question, below program is what I can think of. May be this can help you.
Just to let you know, the overall effect is slightly different from your program. The main difference in effects is, your program gives an effect/impression that each circle are shrinking towards center, as the distance between each circle is always same till it shrinked completely.
My program gives the effect/.impression like the entire board is moving away from your sight till it vanishes. In my program the distance between each circle decreases proportianally till it shrinks.
import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TargetAnimation extends Application {
Button btn = new Button("Animate me!");
StackPane root = new StackPane();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
root.setPrefSize(400, 400);
root.setStyle("-fx-border-color:black;");
Circle board = new Circle();
board.setRadius(200);
board.setStyle("-fx-fill:radial-gradient(focus-angle 0deg , focus-distance 0% , center 50% 50% , radius 21% , repeat, red 44% , white 46% );-fx-stroke-width:1px;-fx-stroke:black;");
root.getChildren().addAll(board, btn);
primaryStage.setScene(new Scene(root));
primaryStage.show();
ScaleTransition transition = new ScaleTransition(Duration.seconds(7), board);
transition.setToX(0);
transition.setToY(0);
btn.setOnAction(e -> {
switch (transition.getStatus()) {
case RUNNING:
transition.pause();
break;
case PAUSED:
transition.play();
break;
default:
board.setScaleX(1);
board.setScaleY(1);
transition.playFromStart();
}
});
}
}
The code given to setOnAction is an EventHandler, which is a #FunctionalInterface with the single method handle. That means that you can give it a lambda expression instead. The method takes an argument, which is the ActionEvent of clicking the button (created for you by JavaFX), and runs the code you give it.
If you want to pause the animation, call Animation#pause, and if you want to resume it, call Animation#play. I suggest that you create a ParallelTransition with all of your ScaleTransitions as its children. Then call the above methods on the ParallelTransition in the event handler.
That means that the setup code, like naming the button and creates the animations, goes outside of the event handler.

How to save selected image in javafx?

like this blank image is saving
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.effect.Light.Point;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class JavaFxSelectPlay extends Application {
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
WebView wv = new WebView();
WebEngine Img = wv.getEngine();
Img.load("https://app.leadlock.pro/upload/69/1019/images/welcome.jpeg");
final Rectangle selection = new Rectangle();
final Point anchor = new Point();
wv.setOnMousePressed(event -> {
anchor.setX(event.getX());
anchor.setY(event.getY());
selection.setX(event.getX());
selection.setY(event.getY());
selection.setFill(null); // transparent
selection.setStroke(Color.BLACK); // border
selection.getStrokeDashArray().add(10.0);
root.getChildren().add(selection);
});
wv.setOnMouseDragged(event -> {
selection.setWidth(Math.abs(event.getX() - anchor.getX()));
selection.setHeight(Math.abs(event.getY() - anchor.getY()));
selection.setX(Math.min(anchor.getX(), event.getX()));
selection.setY(Math.min(anchor.getY(), event.getY()));
});
wv.setOnMouseReleased(event -> {
// Do what you want with selection's properties here
System.out.printf("X: %.2f, Y: %.2f, Width: %.2f, Height: %.2f%n",
selection.getX(), selection.getY(), selection.getWidth(), selection.getHeight());
// root.getChildren().remove(selection);
// selection.setWidth(0);
// selection.setHeight(0);
});
wv.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent e1) -> {
if (e1.getCode() == KeyCode.SPACE ) {
selection.setFill(Color.WHITE); // transparent
}
});
wv.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(final KeyEvent keyEvent) {
if (keyEvent.getCode() == KeyCode.F5) {
System.out.println("F5 pressed");
//Stop letting it do anything else
// WritableImage croppedImage = selection.snapshot(null, null);
WritableImage image = selection.snapshot(new SnapshotParameters(), null);
// TODO: probably use a file chooser here
File file = new File("C:/temp/snapshot.jpg");
try {
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "jpg", file);
} catch (IOException e) {
// TODO: handle exception here
}
}
System.out.println("snapshot saved: " );
}
});
root.getChildren().add(wv);
Scene scene = new Scene(root, 300, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Primary Stage");
primaryStage.show();
}
}
this code i tried and its running fine but the image that is saved is saved with white color its not saved the orginal selected image so how can i do it please help i need to save the selected image and i had done white but not with white color but the selected image so please tell me how to do it and what im doing wrong.
It seems there are two things going on here.
Firstly, instead of taking a snapshot of the Rectangle you are using to mark the selection, you probably want to take a snapshot of the root and use the selection rectangle to specify what part of the snapshot to take. So, instead of writing
WritableImage image = selection.snapshot(new SnapshotParameters(), null);
try writing
SnapshotParameters params = new SnapshotParameters();
params.setViewport(
new Rectangle2D(
selection.getX(),
selection.getY(),
selection.getWidth(),
selection.getHeight()));
root.getChildren().remove(selection);
WritableImage image = root.snapshot(params, null);
I've also removed the rectangle from the root so that it doesn't appear in the output image.
The second thing that happens is that the image comes out pink. This appears to be an existing issue which is supposed to have been fixed in Java 8 but I can still reproduce it in Java 8 update 152. Adapting the code in the question I linked to, you would have to replace the line
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "jpg", file);
with
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
BufferedImage imageRGB = new BufferedImage(
bufferedImage.getWidth(), bufferedImage.getHeight(), BufferedImage.OPAQUE);
Graphics2D graphics = imageRGB.createGraphics();
graphics.drawImage(bufferedImage, 0, 0, null);
ImageIO.write(imageRGB, "jpg", file);

Java/JavaFX Event Handler and setFill() trouble

I was programming a game inspired by Conway's "Game of Life".
Although I have the overall game logic figured out (but not coded), I am still having trouble with getting the fill colors of my rectangle objects to change once the player's first turn is over. When I run my program it skips over the requirement for player one's color (Color.BLUE) and goes straight to player two's color (Color.RED).
Here is the code:
//William Fisher
//July.11.2017
package cellularautomatagame;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.canvas.*;
import javafx.scene.input.MouseEvent;
import static javafx.scene.paint.Color.*;
public class CellularAutomataGame extends Application {
#Override
public void start(Stage stage) {
Group root = new Group();
Scene s = new Scene(root, 300, 300, Color.BLACK);
Canvas canvas = new Canvas(1280,720);
GraphicsContext gc = canvas.getGraphicsContext2D();
root.getChildren().add(canvas);
stage.setScene(s);
stage.show();
gc.setFill(WHITE);
gc.fillRect(0, 0, 5, 720);
gc.fillRect(0, 0, 1280, 5);
gc.fillRect(0, 715, 1280, 5);
gc.fillRect(1275, 0, 5, 720);
Player player1 = new Player();
Player player2 = new Player();
player1.playerFirstMove(root,canvas,Color.BLUE);
player2.playerFirstMove(root,canvas,Color.RED);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
//William Fisher
// July.11.2017
package cellularautomatagame;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import static javafx.scene.paint.Color.*;
import javafx.scene.shape.Rectangle;
public class Player {
int firstMove = 0;
public void playerFirstMove(Group root,Canvas canvas,Color color){
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>(){
#Override
public void handle (MouseEvent e){
while(firstMove < 1){
if(e.getClickCount() == 1){
Rectangle r = new Rectangle(e.getX(),e.getY(),5,5);
r.setFill(color);
root.getChildren().add(r);
firstMove++;
}
}
}
});
firstMove--;
}
}
/** (07/11/2017)Current Problem: The first player is unable to make their first move. Only the
* second player is able to make a first move.
*
* (07/16/2017)Current Problem: Same as previous problem, changed the code so that a rectangle
* object would spawn upon mouse click event. Problem possibly has to do with "setFill()" function.
*/
On line 52 of the main JavaFX method where it shows player1's first turn, it should call the playerFirstMove method and allow a blue rectangle to spawn once the mouse is clicked, as shown in the playerFirstMove method starting on line 18 of the Player class. However when the mouse is clicked, one red rectangle is spawned instead of a blue one. It is as though the program skipped over player1.playerfirstMove(...) on line 52 and went straight to the player2.playerfirstMove(...) on line 53. I've tried for hours to fix this small problem, reading the JavaFX API and searching the internet. The program is doing what I want it to do (spawn only one rectangle per mouse click) but it seems as though it is skipping the instructions on line 52 (player1.playerfirstMove(...)).
To me it seems as though there may be a bug involving the setFill() function?
I would deeply appreciate any help.
Thanks All!
If I understand correctly every mouse click should draw a rectangle of the current player and pass turn to the next player. If so, I reworked your code to have Player with color and draw rectangle logic only:
class Player {
private final Color color;
Player(final Color color) {
this.color = color;
}
void doSomething(final Group root, final double x, final double y) {
Rectangle r = new Rectangle(x, y, 5, 5);
r.setFill(color);
root.getChildren().add(r);
}
}
In main class I have organized cycled iteration (by using Google guava collection utils) and the iterator allows to work only with the current player:
Player player1 = new Player(Color.BLUE);
Player player2 = new Player(Color.RED);
Player player3 = new Player(Color.YELLOW);
final Iterator<Player> playerIterator = Iterators.cycle(player1, player2, player3);
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
if (e.getClickCount() == 1) {
playerIterator.next().doSomething(root, e.getX(), e.getY());
}
});
As result I may have even 3 players and each click triggers only the next one:
BTW, this solution allows to have as many players as needed.

JavaFX 3D depth rendering incorrectly

Setup:
The following code, renders a 3D scene with a visible co-ordinate axis positioned at the origin, from a camera displaced by -156 units in the Z direction. Also, the camera's Z position is a function of mouse scroll, such that scrolling up/down will move the camera further/closer from the origin.
Problem:
upon initial startup of the program, the red and green axis are rendered at/near the origin, when in the physical world, it would be impossible to see them there from the current camera view. (blue axis blocking them). Also, when you scroll backwards and forwards, you can see glitches/flashes where the red/green axis are visible behind the blue axis, which should not occur.
Screenshot of result (with my manual adding of issue description):
initial_screenshot
Question:
1) Is this a problem with my setup? or JavaFX?
2) if this is a problem with my setup, then can someone please explain what I can do to remedy this issue?
Code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package testproblemjavafx01;
/**
*
* #author ad
*/
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.EventHandler;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.stage.Stage;
public class TestProblemJavaFX01 extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
buildAxes(root);
Scene scene = new Scene(root, 600, 400, true, SceneAntialiasing.BALANCED);
PerspectiveCamera camera = new PerspectiveCamera(true);
scene.setFill(Color.WHITE);
camera.setNearClip(0);
camera.setFarClip(1000.0);
camera.setTranslateX(0);
camera.setTranslateY(0);
camera.setTranslateZ(-156);
scene.setCamera(camera);
setMouseEvents(scene);
primaryStage.setResizable(false);
primaryStage.setTitle("TestProblemJavaFX01");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void buildAxes(Group root) {
final PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(Color.DARKRED);
redMaterial.setSpecularColor(Color.RED);
final PhongMaterial greenMaterial = new PhongMaterial();
greenMaterial.setDiffuseColor(Color.DARKGREEN);
greenMaterial.setSpecularColor(Color.GREEN);
final PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.DARKBLUE);
blueMaterial.setSpecularColor(Color.BLUE);
final Box xAxis = new Box(240.0, 1, 1);
final Box yAxis = new Box(1, 240.0, 1);
final Box zAxis = new Box(1, 1, 240.0);
xAxis.setMaterial(redMaterial);
yAxis.setMaterial(greenMaterial);
zAxis.setMaterial(blueMaterial);
root.getChildren().addAll(xAxis, yAxis, zAxis);
}
private void setMouseEvents(final Scene scene) {
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double deltaY = event.getDeltaY();
Camera camera = scene.getCamera();
camera.setTranslateZ(camera.getTranslateZ() + deltaY);
event.consume();
}
});
}
}
I think the problem is within the line camera.setNearClip(0);
From the documentation of setNearClip:
Specifies the distance from the eye of the near clipping plane of this
Camera in the eye coordinate space. Objects closer to the eye than
nearClip are not drawn. nearClip is specified as a value greater than
zero. A value less than or equal to zero is treated as a very small
positive number.
Try to set the value to its default value of 0.1. Or just remove the line.

Web Cam access using JavaFX

I've been searching for a solution to accessing a built-in webcam from a java or javaFX application. I've seen loads of other posts pointing to OpenCV and JavaCV, Sarxos's library and quite a few others.
I've run into difficulties such as newer versions of OpenCV not working with older code posted on various sites and newer code that uses OpenCV 3.0 is hard to find or doesn't do what I need, which is simply a customer application which saves an image taken from the web cam to a variable (or file).
Hope someone can point me in the right direction.
Thanks in advance
You're in luck. I toyed around with OpenCV last weekend and ran into the same problems as you. Here's an example about how to do it. The example opens the camera, uses an AnimationTimer (a bit overkill, but was a quick solution for prototyping) to grab a mat image periodically, converts the mat image to a JavaFX image, performs face detection and paints it on a canvas.
Here's what you need:
Download OpenCV, e. g. in my case the windows version. Rename the opencv-3.0.0.exe to opencv-3.0.0.exe.zip and open it. Extract the contents of build/java.
Create a new JavaFX project. Put the jar and dlls into a lib folder, e. g.:
lib/opencv-300.jar
lib/x64/opencv_java300.dll
Add the jar to your build path.
In your src folder create a path opencv/data/lbpcascades and put the file lbpcascade_frontalface.xml in there (found in etc/lbpcascades). That's only for face detection, you can uncomment the code in case you don't need it.
Create the application class, code:
import java.io.ByteArrayInputStream;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
public class Camera extends Application {
private static final int SCENE_W = 640;
private static final int SCENE_H = 480;
CascadeClassifier faceDetector;
VideoCapture videoCapture;
Canvas canvas;
GraphicsContext g2d;
Stage stage;
AnimationTimer timer;
#Override
public void start(Stage stage) {
this.stage = stage;
initOpenCv();
canvas = new Canvas(SCENE_W, SCENE_H);
g2d = canvas.getGraphicsContext2D();
g2d.setStroke(Color.GREEN);
Group group = new Group(canvas);
Scene scene = new Scene(group, SCENE_W, SCENE_H);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
timer = new AnimationTimer() {
Mat mat = new Mat();
#Override
public void handle(long now) {
videoCapture.read(mat);
List<Rectangle2D> rectList = detectFaces(mat);
Image image = mat2Image(mat);
g2d.drawImage(image, 0, 0);
for (Rectangle2D rect : rectList) {
g2d.strokeRect(rect.getMinX(), rect.getMinY(), rect.getWidth(), rect.getHeight());
}
}
};
timer.start();
}
public List<Rectangle2D> detectFaces(Mat mat) {
MatOfRect faceDetections = new MatOfRect();
faceDetector.detectMultiScale( mat, faceDetections);
System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));
List<Rectangle2D> rectList = new ArrayList<>();
for (Rect rect : faceDetections.toArray()) {
int x = rect.x;
int y = rect.y;
int w = rect.width;
int h = rect.height;
rectList.add(new Rectangle2D(x, y, w, h));
}
return rectList;
}
private void initOpenCv() {
setLibraryPath();
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
videoCapture = new VideoCapture();
videoCapture.open(0);
System.out.println("Camera open: " + videoCapture.isOpened());
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(WindowEvent we) {
timer.stop();
videoCapture.release();
System.out.println("Camera released");
}
});
faceDetector = new CascadeClassifier(getOpenCvResource(getClass(), "/opencv/data/lbpcascades/lbpcascade_frontalface.xml"));
}
public static Image mat2Image(Mat mat) {
MatOfByte buffer = new MatOfByte();
Imgcodecs.imencode(".png", mat, buffer);
return new Image(new ByteArrayInputStream(buffer.toArray()));
}
private static void setLibraryPath() {
try {
System.setProperty("java.library.path", "lib/x64");
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
public static String getOpenCvResource(Class<?> clazz, String path) {
try {
return Paths.get( clazz.getResource(path).toURI()).toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
launch(args);
}
}
Of course you can do whatever you wish (e. g. saving) with the JavaFX image once you have it.
for sarxos, pseudo code, i can't publish whole class:
import com.sleepingdumpling.jvideoinput.Device;
import com.sleepingdumpling.jvideoinput.VideoFrame;
import com.sleepingdumpling.jvideoinput.VideoInput;
Device choosenDevice;
for (Device device : VideoInput.getVideoDevices()) {
// select your choosenDevice webcam here
if (isMyWebcam(device)) {
choosenDevice = device;
break;
}
}
// eg. VideoInput(640,480,25,choosenDevice );
VideoInput videoInput = new VideoInput(frameWidth, frameHeigth,
frameRate, choosenDevice );
VideoFrame vf = null;
while (grabFrames) {
vf = videoInput.getNextFrame(vf);
if (vf != null) {
frameReceived(vf.getRawData());
// or vf.getBufferedImage();
}
}
videoInput.stopSession();

Categories

Resources