I'm trying to use Amazon Rekognition to draw boxes around detected text in an image.
Here's a stripped-down JavaFX app that is supposed to do that:
object TextRekognizeGui extends JFXApp {
lazy val rekognition: Option[AmazonRekognition] =
RekogClient.get(config.AwsConfig.rekogEndpoint, config.AwsConfig.region)
private val config = MainConfig.loadConfig()
stage = new PrimaryStage {
scene = new Scene {
var imgFile: Option[File] = None
content = new HBox {
val imgFile: ObjectProperty[Option[File]] = ObjectProperty(None)
val img: ObjectProperty[Option[Image]] = ObjectProperty(None)
val lines: ObjectProperty[Seq[TextDetection]] = ObjectProperty(Seq.empty)
// These two print statements are always executed when we press the appropriate button.
img.onChange((_, _, newValue) => println("New image"))
lines.onChange((_, _, newValue) => println(s"${newValue.length} new text detections"))
children = Seq(
new VBox {
children = Seq(
new OpenButton(img, imgFile, lines, stage),
new RekogButton(img, imgFile, lines, rekognition)
)
},
new ResultPane(675, 425, img, lines))
}
}
}
stage.show
}
class ResultPane(width: Double, height: Double, img: ObjectProperty[Option[Image]],
textDetections: ObjectProperty[Seq[TextDetection]]) extends AnchorPane { private val emptyPane: Node = Rectangle(0, 0, width, height)
// This print statement, and the one below, are often not executed even when their dependencies change.
private val backdrop = Bindings.createObjectBinding[Node](
() => {println("new backdrop"); img.value.map(new MainImageView(_)).getOrElse(emptyPane)},
img
)
private val outlines = Bindings.createObjectBinding[Seq[Node]](
() => {println("new outlines"); textDetections.value map getTextOutline},
textDetections
)
This looks to me as though I followed the ScalaFX documentation's directions for creating a custom binding. When I click my "RekogButton", I expect to see the message " new text detections" along with the message "new outlines", but more often than not I see only the " new text detections" message.
The behavior appears to be the same throughout the lifetime of the program, but it only manifests on some runs. The same problem happens with the img
-> background
binding, but it manifests less often.
Why do my properties sometimes fail to call the bindings when they change?
edit 1 JavaFX stores the listeners associated with bindings as a weak reference, meaning they can still be garbage-collected unless another object holds a strong or soft reference to them. So it seems my app fails if the garbage collector happens to run between app initialization and the time the outlines are drawn.
This is odd, because I seem to have a strong reference to the binding -- the "delegate" field of the value "outlines" in the result pane. Looking into it more.