Swing 如何在JavaFX应用程序中显示OpenCV网络摄像头捕获帧

Swing 如何在JavaFX应用程序中显示OpenCV网络摄像头捕获帧,swing,kotlin,javafx,qr-code,javacv,Swing,Kotlin,Javafx,Qr Code,Javacv,我正在使用JavaFX创建桌面应用程序,它允许您从网络摄像头扫描二维码 我决定选择JavaCV来处理网络摄像头捕获。但是,问题是,CanvasFrame类创建了一个SwingJFrame。我的主要目标是找到将其与JavaFX组件集成的最佳方法 我的问题是,是否可以在JPanel(或其他Swing/JavaFx组件)中而不是在JFrame中创建CanvasFrame。在这个选项中,我将JPanel包装成SwingNode——它解决了我的集成问题 在我的案例中,我还询问其他解决JavaFX与Java

我正在使用JavaFX创建桌面应用程序,它允许您从网络摄像头扫描二维码

我决定选择JavaCV来处理网络摄像头捕获。但是,问题是,
CanvasFrame
类创建了一个Swing
JFrame
。我的主要目标是找到将其与JavaFX组件集成的最佳方法

我的问题是,是否可以在
JPanel
(或其他Swing/JavaFx组件)中而不是在
JFrame
中创建
CanvasFrame
。在这个选项中,我将
JPanel
包装成
SwingNode
——它解决了我的集成问题

在我的案例中,我还询问其他解决JavaFX与JavaCV集成问题的建议。 也许有一种直接的方法可以将摄像头屏幕嵌入JavaFx组件中

我正在粘贴下面的测试代码。我的代码是用kotlin编写的,但它不会影响问题:

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;
            }

        }
    }
}
import com.google.zxing*
导入com.google.zxing.client.j2se.BufferedImageLuminanceSource
导入com.google.zxing.common.HybridBinarizer
导入org.bytedeco.javacv*
导入java.awt.image.buffereImage
导入java.util*
导入java.util.concurrent.Executors
课堂测试{
伴星{
@JvmStatic
趣味主线(args:Array){
Executors.newSingleThreadExecutor().execute{testWebcam()}
}
私人娱乐测试网络摄像头(){
val抓取器:OpenCVFrameGrabber=OpenCVFrameGrabber(0);
val画布框:画布框=画布框(“Cam”)
grabber.start()
while(canvasFrame.isVisible){
val frame:frame=grabber.grabFrame()?:中断
画布框架。showImage(框架)
解码码(抓取器)
}
}
私有代码(抓取器:OpenCVFrameGrabber){
val java2DFrameConverter:java2DFrameConverter=java2DFrameConverter()
val frame:frame=grabber.grabFrame()
val image=java2DFrameConverter.getBuffereImage(帧)
val decodedQr=parseQr(图像)
println(解码的dqr)
}
private fun parseQr(图像:BuffereImage):字符串{
val读卡器:MultiFormatReader=MultiFormatReader()
val二进制位图:二进制位图=
二进制位图(混合二值化器(缓冲图像亮度源(图像)))
val提示:Hashtable=Hashtable()
提示[DecodeHintType.CHARACTER\u SET]=“UTF-8”
提示[DecodeHintType.可能的\u格式]=listOf(条形码格式.QR\u代码)
回击{
解码(二进制位图,提示)。文本
}捕获(e:NotFoundException){
无效的
}
}
}
}
有一个项目,其中有与Swing、JavaFX一起使用的示例,以及在OpenCV和JavaFX的PixelBuffer之间使用共享内存缓冲区的更新方法

您可以使用JavaFX的ImageView,而不是使用CanvasFrame,它由一个共享ByteBuffer支持。伪代码是:

import java.nio.ByteBuffer
导入javafx.scene.image_
导入org.bytedeco.javacv.Frame
导入org.bytedeco.opencv.global.opencv_imgproc_
导入org.bytedeco.opencv.opencv_core.Mat
val videoView:ImageView=ImageView()
val抓取器:OpenCVFrameGrabber=OpenCVFrameGrabber(0)
grabber.start()
//在相机处于活动状态时,触发一个线程以抓取帧
//每个帧都将被传递到下面的updateView方法
// ... 为简洁起见,省略了计时器/线程
val javaCVMat=Mat()
/**只创建一次缓冲区可节省大量时间*/
val buffer:ByteBuffer=javaCVMat.createBuffer()
val formatByte:WritablePixelFormat=PixelFormat.GetByTebGraphResistence()
乐趣更新视图(帧:帧):单位={
val w=frame.imageWidth()
val h=帧。图像高度()
val mat=javaCVConv.convert(帧)
cvtColor(mat、javaCVMat、COLOR_BGR2BGRA)
val pb=像素缓冲区(w、h、缓冲区、格式化字节)
val wi=可写映像(pb)
videoView.setImage(wi)
}

我解决了我的问题。在我的例子中,最好的解决方案是使用
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)
    }
}
导入javafx.application.application
导入javafx.embed.swing.SwingFXUtils
导入javafx.scene.scene
导入javafx.scene.image.ImageView
导入javafx.scene.image.WritableImage
导入javafx.scene.layout.VBox
导入javafx.stage.stage
导入org.bytedeco.javacv.Frame
导入org.bytedeco.javacv.Java2DFrameConverter
导入org.bytedeco.javacv.OpenCVFrameGrabber
导入java.awt.image.buffereImage
导入java.util.concurrent.Executors
类StackOverflow:Application(){
私有val java2DFrameConverter:java2DFrameConverter=java2DFrameConverter()
伴星{
@JvmStatic
趣味主线(args:Array){
启动(StackOverflow::class.java)
}
}
覆盖乐趣开始(初级阶段:阶段){
val抓取器:OpenCVFrameGrabber=OpenCVFrameGrabber(0)
grabber.start()
val imageView:imageView=imageView()
Executors.newSingleThreadExecutor().execute{
while(true){
val frame=grabber.grabFrame()
imageView.image=frameToImage(帧)
}
}
val场景:场景=场景(VBox(imageView),800.0,800.0)
primaryStage.scene=场景
primaryStage.show()
}
private fun frameToImage(帧:帧):可写图像{
val bufferedImage:bufferedImage=java2DFrameConverter.getBufferedImage(frame)
返回SwingFXUtils.toFXImage(buffereImage,null)
}
}