How to display OpenCV webcam capture frame in JavaFX app - swing

I'm working on creating desktop app using JavaFX, which allows you to scan qr codes from a webcam.
I decided to choose JavaCV to handle webcam capturning. However, the problem is that the CanvasFrame class creates a Swing JFrame. My main goal is to find the best way to integrate this with JavaFX components.
My question is whether it is possible to create CanvasFrame in JPanel(or other Swing/JavaFx component), not in JFrame. In this option I would wrap JPanel into SwingNode - it's solve my integration problem.
I'm also asking for other suggestions that solves JavaFX with JavaCV integration problem in my case.
Maybe there is a direct way to embed a camera screen into a JavaFx component.
I'm pasting the test code below. My code is written in kotlin, but it doesn't affect the problem:
import com.google.zxing.*
import com.google.zxing.client.j2se.BufferedImageLuminanceSource
import com.google.zxing.common.HybridBinarizer
import org.bytedeco.javacv.*
import java.awt.image.BufferedImage
import java.util.*
import java.util.concurrent.Executors
class Test {
companion object {
#JvmStatic
fun main(args: Array<String>) {
Executors.newSingleThreadExecutor().execute { testWebcam() }
}
private fun testWebcam() {
val grabber: OpenCVFrameGrabber = OpenCVFrameGrabber(0);
val canvasFrame: CanvasFrame = CanvasFrame("Cam")
grabber.start()
while (canvasFrame.isVisible) {
val frame: Frame = grabber.grabFrame() ?: break
canvasFrame.showImage(frame)
decodeQrCode(grabber)
}
}
private fun decodeQrCode(grabber: OpenCVFrameGrabber) {
val java2DFrameConverter: Java2DFrameConverter = Java2DFrameConverter()
val frame: Frame = grabber.grabFrame()
val image = java2DFrameConverter.getBufferedImage(frame)
val decodedQr = parseQr(image)
println(decodedQr)
}
private fun parseQr(image: BufferedImage): String? {
val reader: MultiFormatReader = MultiFormatReader()
val binaryBitmap: BinaryBitmap =
BinaryBitmap(HybridBinarizer(BufferedImageLuminanceSource(image)))
val hints: Hashtable<DecodeHintType, Any> = Hashtable()
hints[DecodeHintType.CHARACTER_SET] = "UTF-8"
hints[DecodeHintType.POSSIBLE_FORMATS] = listOf(BarcodeFormat.QR_CODE)
return try {
reader.decode(binaryBitmap, hints).text
} catch (e: NotFoundException) {
null;
}
}
}
}

There's a project at https://github.com/rladstaetter/javacv-webcam that has examples of using javacv with Swing, JavaFX, and a newer method of using a shared memory buffer between OpenCV and JavaFX's PixelBuffer.
Instead of using a CanvasFrame, you can use JavaFX's ImageView backed by a shared ByteBuffer. The pseudo-code is:
import java.nio.ByteBuffer
import javafx.scene.image._
import org.bytedeco.javacv.Frame
import org.bytedeco.opencv.global.opencv_imgproc._
import org.bytedeco.opencv.opencv_core.Mat
val videoView: ImageView = ImageView()
val grabber: OpenCVFrameGrabber = OpenCVFrameGrabber(0)
grabber.start()
// Fire off a thread to grab frames while the camera is active
// Each frame will ber passed to the updateView method below
// ... timer/thread omitted for brevity
val javaCVMat = Mat()
/** create buffer only once saves much time! */
val buffer: ByteBuffer = javaCVMat.createBuffer()
val formatByte: WritablePixelFormat<ByteBuffer> = PixelFormat.getByteBgraPreInstance()
fun updateView(frame: Frame): Unit = {
val w = frame.imageWidth()
val h = frame.imageHeight()
val mat = javaCVConv.convert(frame)
cvtColor(mat, javaCVMat, COLOR_BGR2BGRA)
val pb = PixelBuffer(w, h, buffer, formatByte)
val wi = WritableImage(pb)
videoView.setImage(wi)
}

I solved my problem. In my case, the best solution was to use Java2DFrameConverter:
import javafx.application.Application
import javafx.embed.swing.SwingFXUtils
import javafx.scene.Scene
import javafx.scene.image.ImageView
import javafx.scene.image.WritableImage
import javafx.scene.layout.VBox
import javafx.stage.Stage
import org.bytedeco.javacv.Frame
import org.bytedeco.javacv.Java2DFrameConverter
import org.bytedeco.javacv.OpenCVFrameGrabber
import java.awt.image.BufferedImage
import java.util.concurrent.Executors
class StackOverflow : Application() {
private val java2DFrameConverter: Java2DFrameConverter = Java2DFrameConverter()
companion object {
#JvmStatic
fun main(args: Array<String>) {
launch(StackOverflow::class.java)
}
}
override fun start(primaryStage: Stage) {
val grabber: OpenCVFrameGrabber = OpenCVFrameGrabber(0)
grabber.start()
val imageView: ImageView = ImageView()
Executors.newSingleThreadExecutor().execute {
while (true) {
val frame = grabber.grabFrame()
imageView.image = frameToImage(frame)
}
}
val scene: Scene = Scene(VBox(imageView), 800.0, 800.0)
primaryStage.scene = scene
primaryStage.show()
}
private fun frameToImage(frame: Frame): WritableImage {
val bufferedImage: BufferedImage = java2DFrameConverter.getBufferedImage(frame)
return SwingFXUtils.toFXImage(bufferedImage, null)
}
}

Related

Retrofit 2 - Getting response 200, but list is empty

I've tried to get some json from this api: https://api.cointelegraph.com/api/v1/mobile/feed
I want to get the "title" field of each news. I created the class that supposed to get this fields and wrapper for this class, but I'm getting an empty list instead of this, i guess it's because I have to get field, which called "data" firstly and then parse it. So I changed my class for getting "title" fields to class for getting "data" field, but I'm still getting empty list.
I guess it's kinda stupid mistake, because it works with github api. I will put some kind of response below and my code. Thanks for trying to help.
Json response:
{"data":{"news":[{"type":"header","data":{"id":22358,"title":"MIT, Stanford Researchers to Fund New \u2018Globally Scalable\u2019 Cryptocurrency, \u2018Unit-e\u2019","lead":"Major U.S. universities including MIT, Stanford and UC Berkeley have teamed up to fund a new digital currency.","thumb":"https://s3.cointelegraph.com/storage/uploads/view/f7a7d42a1cba645f861ba810d2524f77.jpg","published_at":{"date":"2019-01-17 18:52:00.000000","timezone_type":3,"timezone":"Europe/London"},"share_url":"https://cointelegraph.com/news/mit-stanford-researchers-to-fund-new-globally-scalable-cryptocurrency-unit-e","views":2375,"type":"news","author":"Helen Partz","author_id":545,"tag":"Blockchain","badge":{"title":"News","label":"default"},"isSponsored":false,"audio":"https://s3.cointelegraph.com/audio/22358.71732d08-dc28-4787-98b1-46c4e0317916.mp3"}},{"type":"currencies","data":[{"price":3677.37,"name":"BTC","currency":"USD","isIncreased":1,"difference":"0.89"},{"price":123.57,"name":"ETH","currency":"USD","isIncreased":1,"difference":"0.17"},{"price":31.56,"name":"LTC","currency":"USD","isIncreased":1,"difference":"0.19"},{"price":0.33,"name":"XRP","currency":"USD","isIncreased":1,"difference":"1.22"}]}
Interface:
package com.hfad.cointask.service
import com.hfad.cointask.model.DataLIst
import com.hfad.cointask.model.NewsList
import retrofit2.Call
import retrofit2.http.*
interface CoinClient {
#GET("api/v1/mobile/feed")
fun getFeed(): Call<NewsList>
}
News class:
package com.hfad.cointask.model
class News (private var data: String) {
fun getData(): String{
return data
}
}
I've tried to get title in first time like that
package com.hfad.cointask.model
class News (private var title: String) {
fun getTitle(): String{
return title
}
}
My wrapper for News class:
package com.hfad.cointask.model
class NewsList {
var list = mutableListOf<News>()
}
My adapter:
package com.hfad.cointask.adapter
import android.support.v7.view.menu.MenuView
import android.support.v7.view.menu.MenuView.ItemView
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.RecyclerView.ViewHolder
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.hfad.cointask.R
import com.hfad.cointask.model.News
class CoinAdapter(var values: List<News>): RecyclerView.Adapter<CoinAdapter.CoinViewHolder>() {
override fun onBindViewHolder(p0: CoinViewHolder, p1: Int) {
val itemTitle: News = values[p1]
p0.newsTitle.text = itemTitle.getData()
}
override fun getItemCount(): Int {
return values.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): CoinViewHolder {
val view: View = LayoutInflater.from(p0.context).inflate(R.layout.news_item_view, p0, false)
return CoinViewHolder(view)
}
class CoinViewHolder(itemView: View): ViewHolder(itemView) {
var newsTitle: TextView = itemView.findViewById(R.id.item_title)
}
}
And my main activity:
package com.hfad.cointask
import android.os.Bundle
import android.support.design.widget.BottomNavigationView
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.widget.Toast
import com.hfad.cointask.adapter.CoinAdapter
import com.hfad.cointask.model.DataLIst
import com.hfad.cointask.model.News
import com.hfad.cointask.model.NewsList
import com.hfad.cointask.service.CoinClient
import kotlinx.android.synthetic.main.activity_feed.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class FeedActivity : AppCompatActivity() {
var news = mutableListOf<News>()
private lateinit var newsRecyclerView: RecyclerView
private lateinit var newsAdapter: CoinAdapter
private lateinit var layoutManager: LinearLayoutManager
private val client = mCoinClient().build()
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
message.setText(R.string.title_home)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
message.setText(R.string.title_dashboard)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
message.setText(R.string.title_notifications)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_feed)
newsRecyclerView = findViewById(R.id.news_recycler_view)
layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
newsRecyclerView.layoutManager = layoutManager
newsAdapter = CoinAdapter(news)
newsRecyclerView.adapter = newsAdapter
getNews().enqueue(object: Callback<NewsList>{
override fun onResponse(call: Call<NewsList>, response: Response<NewsList>) {
var jsonNews = response.body().list
news.clear()
news.addAll(response.body().list)
newsAdapter.notifyDataSetChanged()
}
override fun onFailure(call: Call<NewsList>?, t: Throwable?) {
val toast = Toast.makeText(this#FeedActivity, t.toString(), Toast.LENGTH_SHORT)
toast.show()
}
})
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
private fun getNews() = client.getFeed()
class mCoinClient {
private val builder = Retrofit
.Builder()
.baseUrl("https://api.cointelegraph.com/")
.addConverterFactory(GsonConverterFactory.create())
private val retrofit: Retrofit by lazy {
builder.build()
}
private val client: CoinClient by lazy {
retrofit.create(CoinClient::class.java)
}
fun build() = client
}
}
Thanks for any help
I think it you should change your Interface to
interface CoinClient {
#GET("api/v1/mobile/feed")
fun getFeed(): Call<Your Object that you want to receive it with>
}

NullPointerException when accessing val field in Scala class constructor

I want to translate something like the following Java code into Scala:
private HashMap<KeyStroke,Action>actionMap=new HashMap<KeyStroke,Action>();
KeyStroke left = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
//....
actionMap.put(left, new AbstractAction("move left") {
#Override
public void actionPerformed(ActionEvent e) {
doSomething();
}
}
My initial attempt was this:
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.KeyEvent
import java.awt._
import javax.swing.JFrame
import javax.swing.JPanel
import javax.swing.Action
import javax.swing.AbstractAction
import javax.swing.KeyStroke
import collection.mutable.HashMap
object Main{
def main(args:Array[String]){
val gui:GUI = new GUI()
}
}
class GUI extends JFrame{
initKeyboard
pack
this.setVisible(true)
private val actionMap = new HashMap[KeyStroke,Action]
def initKeyboard{
val left = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0)
actionMap.put(left, new AbstractAction("Move Left"){
override def actionPerformed(e:ActionEvent){
println("Do something")
}
})//actionMap.put
}
}
Note that I have not yet written code to do anything with the actionMap.
However I get the following error at runtime:
Java.lang.NullPointerException
at GUI.initKeyboard(Game.scala:24)
at GUI.<init>(Game.scala:18)
at Main$.main(Game.scala:13)
at Main.main(Game.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
(note that line 24 is the line that starts "actionMap.put")
How should this be implemented?
The problem is in the initialization order.
The following is executed on new GUI instance creation, in the order listed:
initKeyboard
pack
this.setVisible(true)
private val actionMap = new HashMap[KeyStroke,Action]
You can see that you call initKeyboard before you initialize actionMap. Therefore accessing it inside initKeyboard throws an exception.
You can verify the initialization order with this simple example:
class GUI extends {
initKeyboard
private val actionMap = println("actionMap")
def initKeyboard: Unit = {
println("initKeyboard")
}
}
new GUI // prints: initKeyboard actionMap

How to avoid garbage collection when using Json?

I'm currently loading my level with the Json classes. Problem is, the application lags at the beginning which is probably due to the GarbageCollector removing all my unused JsonValue objects. I don't currently know, how to fix this, any advice would be appreciated,
the WorldLoader class with the json stuff, code looks like much, but is pretty straight forward:
package com.Sidescroll.game.Helpers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.Sidescroll.game.GameObjects.Background;
import com.Sidescroll.game.GameObjects.Platform;
import com.Sidescroll.game.GameObjects.Player;
import com.Sidescroll.game.GameWorld.GameWorld;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.JsonReader;
import com.badlogic.gdx.utils.JsonValue;
public final class WorldLoader {
private WorldLoader() {}
public static HashMap<String, GameWorld> worlds = new HashMap<String, GameWorld>();
private static JsonValue jsonPlatforms, jsonSpawn, jsonBackgrounds;
public static GameWorld loadWorld(String levelName) {
if(worlds.get(levelName) != null) return worlds.get(levelName);
//load json file
FileHandle handle = Gdx.files.internal("levels/" + levelName + ".json");
String fileContent = handle.readString();
JsonValue json = new JsonReader().parse(fileContent);
jsonPlatforms = json.get("platforms");
jsonBackgrounds = json.get("backgrounds");
jsonSpawn = json.get("spawn");
//loads platforms
List<Platform> platforms = new ArrayList<Platform>();
for(JsonValue pValue : jsonPlatforms.iterator()) {
Platform platform = new Platform(pValue.getInt("x"),
pValue.getInt("y"),
pValue.getInt("width"),
pValue.getInt("height"),
Platform.Type.valueOf(Platform.Type.class, pValue.getString("type")),
pValue.getString("variant"));
platforms.add(platform);
}
//create player
Vector2 vecSpawn = new Vector2(jsonSpawn.getInt("x"), jsonSpawn.getInt("y"));
Player player = new Player(vecSpawn, platforms);
//loads backgrounds
List<Background> backgrounds = new ArrayList<Background>();
for(JsonValue bgValue : jsonBackgrounds.iterator()) {
Background bg = new Background("bin/sprites/" + bgValue.getString("name") + ".png",
bgValue.getInt("x"),
bgValue.getInt("y"),
bgValue.getInt("width"),
bgValue.getInt("height"),
bgValue.getBoolean("front"));
backgrounds.add(bg);
}
worlds.put(levelName, new GameWorld(player, platforms, backgrounds));
return worlds.get(levelName);
}
}

Akka-Spray object marshall

I am experimenting with aka and spray, what I want to achieve is a simple object marshalling service.
When I try to compile the code I get the following error :
Error:(33, 18) could not find implicit value for parameter marshaller:
spray.httpx.marshalling.Marshaller[ExampleApplication.Password]
marshal(Password(randomString(8),i,0))
^
Here is the code:
import akka.actor.ActorSystem
import spray.http.HttpEntity
import spray.json.DefaultJsonProtocol
import spray.routing.SimpleRoutingApp
import spray.httpx.marshalling._
import spray.json._
object ExampleApplication extends App with SimpleRoutingApp {
implicit val actorSystem = ActorSystem()
implicit var i = 0;
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val PasswordFormat = jsonFormat3(Password)
}
case class Password(pass: String, count: Int, complexity: Int)
def newPass(cplx: Int):Password = {return Password(randomString(cplx),i,0)}
startServer(interface = "localhost", port = 8080) {
get {
path("passgen") {
i+=1
complete {
marshal(newPass(8))
}
}
}
}
def randomString(n: Int): String = {
n match {
case 1 => util.Random.nextPrintableChar().toString
case _ => util.Random.nextPrintableChar.toString ++ randomString(n - 1).toString
}
}
}
I'm still failing to understand what's going wrong.
Two changes to fix it:
import spray.httpx.SprayJsonSupport._
Then even though you define the JsonProtocol object right in your app you must still import it's members explicitly:
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val PasswordFormat = jsonFormat3(Password)
}
import MyJsonProtocol._
That looks a little repetitive in this case, but in most use cases you'll have it defined somewhere else.
Optional
You can complete the call without explicitly calling marshal:
complete {
newPass(8)
}

How to interrupt the window closing mechanism in scala swing

I'm building a GUI with the SimpleSwingApplication trait in scala swing.
What I want to do is to provide a mechanism on close, that asks the user (Yes,No,Cancel) if he didn't save the document yet. If the user hits Cancel the Application shouldn't close. But everything I tried so far with MainFrame.close and closeOperation didn't work.
So how is this done in Scala Swing?
I'm on Scala 2.9.
Thanks in advance.
Slightly different variation from what Howard suggested
import scala.swing._
object GUI extends SimpleGUIApplication {
def top = new Frame {
title="Test"
import javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE
peer.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE)
override def closeOperation() { showCloseDialog() }
private def showCloseDialog() {
Dialog.showConfirmation(parent = null,
title = "Exit",
message = "Are you sure you want to quit?"
) match {
case Dialog.Result.Ok => exit(0)
case _ => ()
}
}
}
}
By using DO_NOTHING_ON_CLOSE you are given a chance to define what should be done when a WindowEvent.WINDOW_CLOSING event is received by the scala frame. When a scala frame receives a WINDOW_CLOSING event it reacts by calling closeOperation. Hence, to display a dialog when the user attempts to close the frame it is enough to override closeOperation and implement the desired behavior.
What about this:
import swing._
import Dialog._
object Test extends SimpleSwingApplication {
def top = new MainFrame {
contents = new Button("Hi")
override def closeOperation {
visible = true
if(showConfirmation(message = "exit?") == Result.Ok) super.closeOperation
}
}
}
I am not really familiar with scala swing but I found this code in some of my old test programs:
object GUI extends SimpleGUIApplication {
def top = new Frame {
title="Test"
peer.setDefaultCloseOperation(0)
reactions += {
case WindowClosing(_) => {
println("Closing it?")
val r = JOptionPane.showConfirmDialog(null, "Exit?")
if (r == 0) sys.exit(0)
}
}
}
}
This does what I wanted to do; calling super.closeOperation didn't close the frame. I would have just said that in a comment, but I am not yet allowed.
object FrameCloseStarter extends App {
val mainWindow = new MainWindow()
mainWindow.main(args)
}
class MainWindow extends SimpleSwingApplication {
def top = new MainFrame {
title = "MainFrame"
preferredSize = new Dimension(500, 500)
pack()
open()
def frame = new Frame {
title = "Frame"
preferredSize = new Dimension(500, 500)
location = new Point(500,500)
pack()
peer.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE)
override def closeOperation() = {
println("Closing")
if (Dialog.showConfirmation(message = "exit?") == Result.Ok) {
peer.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
close()
}
}
}
frame.open()
}
}