带有Play 2.0和Scala的二进制websocket

在最近的文章中,我展示了如何一起使用webrtc,canvas和websockets创建面部检测应用程序,其前端完全在浏览器中运行,而无需插件。 在那篇文章中,我使用了基于Jetty的后端通过JavaCV包装器使用OpenCV处理图像分析。

当我几乎完成本文时,我注意到Play 2.0也支持websockets。 我真的很喜欢在Play和Scala中进行开发,因此作为一个实验,我将后端部分从Jetty / Java / JavaCV堆栈重写为Play2.0 / Scala / JavaCV堆栈。 如果您想自己做,请确保从此处开始使用前端代码。 由于前端代码除了websocket正在侦听的位置以外没有其他更改。

设置Play 2.0环境

我不会过多地谈论如何启动Play 2.0 / Scala项目。 如果您需要更多信息,可以在我的其他一些帖子中找到详细信息。 我们要做的是设置JavaCV的依赖关系,以便可以在Play 2中使用它们。我已将它们手动添加到本地ivy存储库中,以便可以像其他任何依赖关系一样从sbt配置中引用它们。 对于我的示例,我为JavaCV库创建了以下目录布局:

./play-2.0-RC2/repository/cache/javacv
./play-2.0-RC2/repository/cache/javacv/javacpp
./play-2.0-RC2/repository/cache/javacv/javacpp/ivy-2.3.1.xml
./play-2.0-RC2/repository/cache/javacv/javacpp/ivydata-2.3.1.properties
./play-2.0-RC2/repository/cache/javacv/javacv
./play-2.0-RC2/repository/cache/javacv/javacv/ivy-2.3.1.xml
./play-2.0-RC2/repository/cache/javacv/javacv/ivydata-2.3.1.properties
./play-2.0-RC2/repository/cache/javacv/javacv-macosx-x86_64
./play-2.0-RC2/repository/cache/javacv/javacv-macosx-x86_64/ivy-2.3.1.xml
./play-2.0-RC2/repository/cache/javacv/javacv-macosx-x86_64/ivydata-2.3.1.properties
./play-2.0-RC2/repository/local/javacv
./play-2.0-RC2/repository/local/javacv/javacpp
./play-2.0-RC2/repository/local/javacv/javacpp/2.3.1
./play-2.0-RC2/repository/local/javacv/javacpp/2.3.1/ivys
./play-2.0-RC2/repository/local/javacv/javacpp/2.3.1/ivys/ivy.xml
./play-2.0-RC2/repository/local/javacv/javacpp/2.3.1/jars
./play-2.0-RC2/repository/local/javacv/javacpp/2.3.1/jars/javacpp.jar
./play-2.0-RC2/repository/local/javacv/javacv
./play-2.0-RC2/repository/local/javacv/javacv/2.3.1
./play-2.0-RC2/repository/local/javacv/javacv/2.3.1/ivys
./play-2.0-RC2/repository/local/javacv/javacv/2.3.1/ivys/ivy.xml
./play-2.0-RC2/repository/local/javacv/javacv/2.3.1/jars
./play-2.0-RC2/repository/local/javacv/javacv/2.3.1/jars/javacv.jar
./play-2.0-RC2/repository/local/javacv/javacv-macosx-x86_64
./play-2.0-RC2/repository/local/javacv/javacv-macosx-x86_64/2.3.1
./play-2.0-RC2/repository/local/javacv/javacv-macosx-x86_64/2.3.1/ivys
./play-2.0-RC2/repository/local/javacv/javacv-macosx-x86_64/2.3.1/ivys/ivy.xml
./play-2.0-RC2/repository/local/javacv/javacv-macosx-x86_64/2.3.1/jars
./play-2.0-RC2/repository/local/javacv/javacv-macosx-x86_64/2.3.1/jars/javacv-macosx-x86_64.jar

从清单中可以看到,我只是将三个javacv提供的jar添加到了本地存储库中。 我还添加了一个最小的ivy.xml,以便可以在ivy和sbt中使用它们。 这个最小的ivy.xml看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<ivy-module version="2.0" xmlns:m="http://ant.apache.org/ivy/maven">
    <info
        organisation="javacv"
        module="javacv-macosx-x86_64"
        revision="2.3.1"
        status="release">
        </info>

        <publications>
            <artifact type="jar"/>
        </publications>
</ivy-module>

将这些文件添加到我的存储库后,我可以在Build.scala文件中设置Play 2.0项目的依赖项。

import sbt._
import Keys._
import PlayProject._

object ApplicationBuild extends Build {

    val appName         = "PlayWebsocketJavaCV"
    val appVersion      = "1.0-SNAPSHOT"

    val appDependencies = Seq(
      "javacv" % "javacv" % "2.3.1",
      "javacv" % "javacpp" % "2.3.1",
      "javacv" % "javacv-macosx-x86_64" % "2.3.1"
    )

    val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
      // Add your own project settings here      
    )
}

现在运行“ play update”和“ play eclipsify”以更新依赖关系和Eclipse配置(如果使用的是Eclipse)。

在Play中配置WebSocket

在Play 2.0中使用websocket非常简单。 您需要做的第一件事是将URL添加到conf目录中的路由配置中。

GET     /wsrequest                  controllers.Application.wsrequest

并且,当然,您需要实施此路线指向的操作:

/**
 * Simple websocket listener configured in play 2. This uses a synchronous model, where
 * the same channel is used to send the response. For this usecase this is useful, if
 * we want async processing we could have used Akka actors together with play 2.0 async
 * support.
 */
def wsrequest = WebSocket.using[Array[Byte]] { request =>

  // Create the outbound value that is called for each
  val out = Enumerator.imperative[Array[Byte]]();

 val in = Iteratee.foreach[Array[Byte]](content => {
    out.push(FaceDetect.detect(content));
  })

  // tie the in and out values to each other
  (in, out)
}

在此代码中,我们配置一个输入通道(输入)和一个输出通道(输出),并将它们连接到插座。 每当HTML5客户端通过websocket发送请求时,都会调用“ in”方法,而当我们想向客户端发送内容时,可以使用“ out”通道。 需要将“ in”频道定义为Iteratee(更多信息,请参见这些 Play文档)。 它的作用是,对于收到的每个输入消息,我们都运行specifici方法。 在这种情况下,我们运行FaceDetect.detect操作(稍后将对此进行详细介绍),并且使用“ out”通道将该操作的结果推回客户端。 此“输出”通道本身被定义为枚举器(请参见这些播放文档)。 如果我们希望将此枚举数附加到侦听器,则可以附加不同的侦听器,但是在这种情况下,我们对消息不执行任何操作,只需将其传递给客户端即可。

从Scala使用JavaCV

最后一步是FaceDetect.detect函数的代码。 Java版本(请参阅前面提到的文章 )很容易转换为scala版本。

package javacv

import com.googlecode.javacv.cpp.opencv_core._
import com.googlecode.javacv.cpp.opencv_imgproc._
import com.googlecode.javacv.cpp.opencv_highgui._
import com.googlecode.javacv.cpp.opencv_objdetect._
import com.googlecode.javacpp.BytePointer
import java.nio.ByteBuffer
import javax.imageio.ImageIO
import java.io.ByteArrayOutputStream
import scala.tools.nsc.io.VirtualFile

object FaceDetect {

 var CASCADE_FILE =PATH_TO_CASCADE_FILE;
   var minsize = 20;
 var group = 0;
 var scale = 1.1;

  def detect(imageData:Array[Byte]) : Array[Byte] = {

    // we need to wrap the input array, since BytePointer doesn't accept
    // a bytearray as input. It accepts a byte varargs, but Array[Byte]
    // doesn't convert automatically
    var wrappedData = ByteBuffer.wrap(imageData);
    var originalImage = cvDecodeImage(cvMat(1, imageData.length,CV_8UC1, new BytePointer(wrappedData)));

    // convert to grayscale for easy detection
    var grayImage = IplImage.create(originalImage.width(), originalImage.height(), IPL_DEPTH_8U, 1);
    cvCvtColor(originalImage, grayImage, CV_BGR2GRAY);

    // storage is needed to store information during detection
 var storage = CvMemStorage.create();

 // load and run the cascade
 var cascade = new CvHaarClassifierCascade(cvLoad(CASCADE_FILE));
 var faces = cvHaarDetectObjects(grayImage, cascade, storage, scale, group, minsize);

 // draw a rectangle for the detected faces
 for (i <- 0 until faces.total) {
   var r = new CvRect(cvGetSeqElem(faces, i));
   cvRectangle(originalImage, cvPoint(r.x, r.y),
     cvPoint(r.x + r.width(), r.y + r.height),
     CvScalar.YELLOW, 1, CV_AA, 0);
 }

 // convert to bytearray and return
 var bout = new ByteArrayOutputStream();
 var imgb = originalImage.getBufferedImage();
 ImageIO.write(imgb, "png", bout);

    bout.toByteArray()
  }
}

我遇到的唯一问题是BytePointer构造函数。 签名之一接受字节类型的变量。 在Java中,这允许我只为该构造函数提供一个byte [],而在Scala中,这是行不通的。 幸运的是,也可以使用ByteBuffer创建BytePointer。 对于其余部分,这是Java到Scala的一对一转换。

运行代码

仅此而已。 默认情况下,play在端口9000上侦听,在基于Jetty的示例中,我们使服务器在9999上运行并侦听根上下文。 要使用我们基于Play2 / Scala的服务器,我们只需要将浏览器指向正确的websocket服务器url。

ws = new WebSocket("ws://127.0.0.1:9000/wsrequest");

现在,当我们运行它时,我们将Play 2用作服务器,并使用scal运行JavaCV代码。 更重要的是,它仍然有效:

参考:来自Smart Java博客的JCG合作伙伴 Jos Dirksen的带有Play 2.0和Scala的二进制websocket(以及Java CV / OpenCV)


翻译自: https://www.javacodegeeks.com/2012/05/binary-websockets-with-play-20-and.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值