Skip to content

Commit e75b1f6

Browse files
authored
Dynamically resize image previews to fit the pane (#711)
1 parent cef0c59 commit e75b1f6

File tree

9 files changed

+171
-130
lines changed

9 files changed

+171
-130
lines changed

ui/src/main/java/edu/wpi/grip/ui/preview/BlobsSocketPreviewView.java

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
package edu.wpi.grip.ui.preview;
22

3-
import edu.wpi.grip.core.events.RenderEvent;
43
import edu.wpi.grip.core.operations.composite.BlobsReport;
54
import edu.wpi.grip.core.sockets.OutputSocket;
65
import edu.wpi.grip.ui.util.GripPlatform;
7-
import edu.wpi.grip.ui.util.ImageConverter;
86

9-
import com.google.common.eventbus.Subscribe;
10-
11-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12-
13-
import javafx.application.Platform;
147
import javafx.geometry.Orientation;
158
import javafx.scene.control.CheckBox;
169
import javafx.scene.control.Label;
1710
import javafx.scene.control.Separator;
1811
import javafx.scene.image.Image;
19-
import javafx.scene.image.ImageView;
2012
import javafx.scene.layout.VBox;
2113

2214
import static org.bytedeco.javacpp.opencv_core.LINE_8;
@@ -32,17 +24,13 @@
3224
* A SocketPreviewView for BlobsReports that shows the original image with circles overlayed onto
3325
* it, showing the location and size of detected blobs.
3426
*/
35-
public class BlobsSocketPreviewView extends SocketPreviewView<BlobsReport> {
27+
public final class BlobsSocketPreviewView extends ImageBasedPreviewView<BlobsReport> {
3628

37-
private final ImageConverter imageConverter = new ImageConverter();
38-
private final ImageView imageView = new ImageView();
3929
private final Label infoLabel = new Label();
4030
private final Mat tmp = new Mat();
4131
private final Point point = new Point();
4232
private final GripPlatform platform;
4333
@SuppressWarnings("PMD.ImmutableField")
44-
@SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC",
45-
justification = "Do not need to synchronize inside of a constructor")
4634
private boolean showInputImage = false;
4735

4836
/**
@@ -54,26 +42,20 @@ public BlobsSocketPreviewView(GripPlatform platform, OutputSocket<BlobsReport> s
5442
final CheckBox show = new CheckBox("Show Input Image");
5543
show.setSelected(this.showInputImage);
5644
show.selectedProperty().addListener(observable -> {
57-
this.showInputImage = show.isSelected();
58-
this.convertImage();
45+
synchronized (this) {
46+
this.showInputImage = show.isSelected();
47+
this.convertImage();
48+
}
5949
});
6050

6151
final VBox content = new VBox(this.imageView, new Separator(Orientation.HORIZONTAL), this
6252
.infoLabel, show);
6353
content.getStyleClass().add("preview-box");
6454
this.setContent(content);
65-
66-
assert Platform.isFxApplicationThread() : "Must be in FX Thread to create this or you will be"
67-
+ " exposing constructor to another thread!";
68-
}
69-
70-
71-
@Subscribe
72-
public void onRender(RenderEvent event) {
73-
this.convertImage();
7455
}
7556

76-
private void convertImage() {
57+
@Override
58+
protected void convertImage() {
7759
synchronized (this) {
7860
final BlobsReport blobsReport = this.getSocket().getValue().get();
7961
final Mat input = blobsReport.getInput();
@@ -101,7 +83,7 @@ private void convertImage() {
10183
final Mat output = tmp;
10284
final int numBlobs = blobsReport.getBlobs().size();
10385
platform.runAsSoonAsPossible(() -> {
104-
final Image image = this.imageConverter.convert(output);
86+
final Image image = this.imageConverter.convert(output, getImageHeight());
10587
this.imageView.setImage(image);
10688
this.infoLabel.setText("Found " + numBlobs + " blobs");
10789
});

ui/src/main/java/edu/wpi/grip/ui/preview/ContoursSocketPreviewView.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
package edu.wpi.grip.ui.preview;
22

3-
import edu.wpi.grip.core.events.RenderEvent;
43
import edu.wpi.grip.core.operations.composite.ContoursReport;
54
import edu.wpi.grip.core.sockets.OutputSocket;
65
import edu.wpi.grip.ui.util.GripPlatform;
76
import edu.wpi.grip.ui.util.ImageConverter;
87

9-
import com.google.common.eventbus.Subscribe;
10-
11-
import javafx.application.Platform;
128
import javafx.scene.control.CheckBox;
139
import javafx.scene.control.Label;
1410
import javafx.scene.image.Image;
@@ -26,7 +22,7 @@
2622
* outline (so they can be individually distinguished), as well as a count of the total number of
2723
* contours found.
2824
*/
29-
public final class ContoursSocketPreviewView extends SocketPreviewView<ContoursReport> {
25+
public final class ContoursSocketPreviewView extends ImageBasedPreviewView<ContoursReport> {
3026

3127
private static final Scalar[] CONTOUR_COLORS = new Scalar[]{
3228
Scalar.RED,
@@ -54,19 +50,11 @@ public ContoursSocketPreviewView(GripPlatform platform, OutputSocket<ContoursRep
5450

5551
this.setContent(new VBox(this.imageView, this.infoLabel, this.colorContours));
5652

57-
this.colorContours.selectedProperty().addListener(observable -> this.render());
58-
59-
assert Platform.isFxApplicationThread() : "Must be in FX Thread to create this or you will be"
60-
+ " exposing constructor to another thread!";
61-
render();
62-
}
63-
64-
@Subscribe
65-
public void onRender(RenderEvent event) {
66-
this.render();
53+
this.colorContours.selectedProperty().addListener(observable -> this.convertImage());
6754
}
6855

69-
private void render() {
56+
@Override
57+
protected void convertImage() {
7058
synchronized (this) {
7159
final ContoursReport contours = this.getSocket().getValue().get();
7260
long numContours = 0;
@@ -93,7 +81,7 @@ private void render() {
9381
final long finalNumContours = numContours;
9482
final Mat convertInput = tmp;
9583
platform.runAsSoonAsPossible(() -> {
96-
final Image image = this.imageConverter.convert(convertInput);
84+
final Image image = this.imageConverter.convert(convertInput, getImageHeight());
9785
this.imageView.setImage(image);
9886
this.infoLabel.setText("Found " + finalNumContours + " contours");
9987
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package edu.wpi.grip.ui.preview;
2+
3+
import edu.wpi.grip.core.events.RenderEvent;
4+
import edu.wpi.grip.core.sockets.OutputSocket;
5+
import edu.wpi.grip.ui.util.ImageConverter;
6+
7+
import com.google.common.eventbus.Subscribe;
8+
9+
import javafx.application.Platform;
10+
import javafx.scene.image.ImageView;
11+
12+
/**
13+
* Base class for image previews.
14+
*/
15+
public abstract class ImageBasedPreviewView<T> extends SocketPreviewView<T> {
16+
17+
/**
18+
* Image converter for converting OpenCV mats to JavaFX images.
19+
*/
20+
protected final ImageConverter imageConverter = new ImageConverter();
21+
22+
/**
23+
* The view showing the image.
24+
*/
25+
protected final ImageView imageView = new ImageView();
26+
27+
private int imageHeight = 1;
28+
29+
/**
30+
* @param socket An output socket to preview.
31+
*/
32+
protected ImageBasedPreviewView(OutputSocket<T> socket) {
33+
super(socket);
34+
assert Platform.isFxApplicationThread() : "Must be in FX Thread to create this or you will be"
35+
+ " exposing constructor to another thread!";
36+
}
37+
38+
/**
39+
* Gets the height of the image to render.
40+
*/
41+
protected final int getImageHeight() {
42+
return imageHeight;
43+
}
44+
45+
/**
46+
* Converts the input data to an image and render it in the {@link #imageView}.
47+
*/
48+
protected abstract void convertImage();
49+
50+
/**
51+
* Updates the image preview when the pipeline runs.
52+
*/
53+
@Subscribe
54+
public final void onRenderEvent(RenderEvent e) {
55+
convertImage();
56+
}
57+
58+
/**
59+
* Resizes the image based on the given height while preserving the ratio.
60+
*/
61+
public final void resize(int imageHeight) {
62+
this.imageHeight = imageHeight;
63+
convertImage();
64+
}
65+
66+
}

ui/src/main/java/edu/wpi/grip/ui/preview/ImageSocketPreviewView.java

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,35 @@
11
package edu.wpi.grip.ui.preview;
22

3-
import edu.wpi.grip.core.events.RenderEvent;
43
import edu.wpi.grip.core.sockets.OutputSocket;
54
import edu.wpi.grip.ui.util.GripPlatform;
6-
import edu.wpi.grip.ui.util.ImageConverter;
75

8-
import com.google.common.eventbus.Subscribe;
9-
10-
import javafx.application.Platform;
116
import javafx.scene.image.Image;
12-
import javafx.scene.image.ImageView;
137

148
import static org.bytedeco.javacpp.opencv_core.Mat;
159

1610
/**
1711
* A <code>SocketPreviewView</code> that previews sockets containing OpenCV Mats.
1812
*/
19-
public class ImageSocketPreviewView extends SocketPreviewView<Mat> {
13+
public class ImageSocketPreviewView extends ImageBasedPreviewView<Mat> {
2014

2115
private final GripPlatform platform;
22-
private final ImageConverter imageConverter;
23-
private final ImageView imageView;
2416

2517
/**
2618
* @param socket An output socket to preview.
2719
*/
2820
ImageSocketPreviewView(GripPlatform platform, OutputSocket<Mat> socket) {
2921
super(socket);
3022
this.platform = platform;
31-
this.imageConverter = new ImageConverter();
32-
this.imageView = new ImageView();
3323
this.setContent(imageView);
34-
35-
assert Platform.isFxApplicationThread() : "Must be in FX Thread to create this or you will be"
36-
+ " exposing constructor to another thread!";
37-
38-
convertImage();
39-
}
40-
41-
@Subscribe
42-
public void onRender(RenderEvent event) {
43-
convertImage();
4424
}
4525

46-
private void convertImage() {
26+
@Override
27+
protected void convertImage() {
4728
synchronized (this) {
4829
this.getSocket().getValue().ifPresent(mat -> {
4930
platform.runAsSoonAsPossible(() -> {
50-
final Image image = this.imageConverter.convert(mat);
51-
this.imageView.setImage(image);
31+
Image image = imageConverter.convert(mat, getImageHeight());
32+
imageView.setImage(image);
5233
});
5334
});
5435
}

ui/src/main/java/edu/wpi/grip/ui/preview/LinesSocketPreviewView.java

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
package edu.wpi.grip.ui.preview;
22

3-
import edu.wpi.grip.core.events.RenderEvent;
43
import edu.wpi.grip.core.operations.composite.LinesReport;
54
import edu.wpi.grip.core.sockets.OutputSocket;
65
import edu.wpi.grip.ui.util.GripPlatform;
76
import edu.wpi.grip.ui.util.ImageConverter;
87

9-
import com.google.common.eventbus.Subscribe;
10-
11-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12-
138
import java.util.List;
14-
import javafx.application.Platform;
9+
1510
import javafx.geometry.Orientation;
1611
import javafx.scene.control.CheckBox;
1712
import javafx.scene.control.Label;
@@ -34,7 +29,7 @@
3429
* A <code>SocketPreviewView</code> that previews sockets containing containing the result of a line
3530
* detection algorithm.
3631
*/
37-
public class LinesSocketPreviewView extends SocketPreviewView<LinesReport> {
32+
public final class LinesSocketPreviewView extends ImageBasedPreviewView<LinesReport> {
3833

3934
private final ImageConverter imageConverter = new ImageConverter();
4035
private final ImageView imageView = new ImageView();
@@ -44,8 +39,6 @@ public class LinesSocketPreviewView extends SocketPreviewView<LinesReport> {
4439
private final Point endPoint = new Point();
4540
private final GripPlatform platform;
4641
@SuppressWarnings("PMD.ImmutableField")
47-
@SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC",
48-
justification = "Do not need to synchronize inside of a constructor")
4942
private boolean showInputImage = false;
5043

5144
/**
@@ -59,26 +52,20 @@ public LinesSocketPreviewView(GripPlatform platform, OutputSocket<LinesReport> s
5952
final CheckBox show = new CheckBox("Show Input Image");
6053
show.setSelected(this.showInputImage);
6154
show.selectedProperty().addListener(observable -> {
62-
this.showInputImage = show.isSelected();
63-
this.convertImage();
55+
synchronized (this) {
56+
this.showInputImage = show.isSelected();
57+
this.convertImage();
58+
}
6459
});
6560

6661
final VBox content = new VBox(this.imageView, new Separator(Orientation.HORIZONTAL), this
6762
.infoLabel, show);
6863
content.getStyleClass().add("preview-box");
6964
this.setContent(content);
70-
71-
assert Platform.isFxApplicationThread() : "Must be in FX Thread to create this or you will be"
72-
+ " exposing constructor to another thread!";
73-
convertImage();
74-
}
75-
76-
@Subscribe
77-
public void onRender(RenderEvent event) {
78-
this.convertImage();
7965
}
8066

81-
private void convertImage() {
67+
@Override
68+
protected void convertImage() {
8269
synchronized (this) {
8370
final LinesReport linesReport = this.getSocket().getValue().get();
8471
final List<LinesReport.Line> lines = linesReport.getLines();
@@ -113,7 +100,7 @@ private void convertImage() {
113100
final Mat convertInput = input;
114101
final int numLines = lines.size();
115102
platform.runAsSoonAsPossible(() -> {
116-
final Image image = this.imageConverter.convert(convertInput);
103+
final Image image = this.imageConverter.convert(convertInput, getImageHeight());
117104
this.imageView.setImage(image);
118105
this.infoLabel.setText("Found " + numLines + " lines");
119106
});

0 commit comments

Comments
 (0)