java - 高效实现 Java native 接口(interface)网络摄像头源

标签 java performance opencv java-native-interface webcam

我正在从事一个项目,该项目从网络摄像头获取视频输入并向用户显示运动区域。我在这个项目中的“测试版”尝试是使用 Java Media Framework 来检索网络摄像头提要。通过一些实用函数,JMF 方便地将网络摄像头帧作为 BufferedImages 返回,我围绕它构建了大量框架来处理。但是,我很快意识到 Sun/Oracle 不再很好地支持 JMF,并且无法通过 JMF 界面访问一些更高的网络摄像头分辨率 (720p)。

我想继续将帧作为 BufferedImages 进行处理,并使用 OpenCV (C++) 获取视频源。单独使用 OpenCV 的框架,我发现 OpenCV 在高效返回高清网络摄像头帧并将它们绘制到屏幕上做得很好。

我认为将这些数据输入 Java 并实现相同的效率会非常简单。我刚刚编写完 JNI DLL 以将此数据复制到 BufferedImage 并将其返回给 Java。但是,我发现我正在执行的数据复制量确实会影响性能。我的目标是 30 FPS,但仅将 OpenCV 返回的 char 数组中的数据复制到 Java BufferedImage 中就需要大约 100 毫秒。相反,我看到大约 2-5 FPS。

当返回帧捕获时,OpenCV 提供一个指向一维字符数组的指针。这些数据需要提供给 Java,显然我没有时间复制任何数据。

我需要一个更好的解决方案来将这些帧捕捉到 BufferedImage 中。我正在考虑的几个解决方案,我认为都不是很好(相当肯定它们也会表现不佳):

(1) 覆盖 BufferedImage,并通过对 DLL 进行 native 调用从各种 BufferedImage 方法返回像素数据。 (我没有立即进行数组复制,而是按照调用代码的要求返回单个像素)。请注意,调用代码通常需要图像中的所有像素来绘制图像或对其进行处理,因此这种单独的像素抓取操作将在 2D for 循环中实现。

(2) 指示 BufferedImage 使用 java.nio.ByteBuffer 以某种方式直接访问 OpenCV 返回的 char 数组中的数据。非常感谢有关如何完成此操作的任何提示。

(3) 一切用C++做,忘记Java。好吧,是的,这听起来确实是最合乎逻辑的解决方案,但是我没有时间从头开始这个耗时数月的项目。

截至目前,我的 JNI 代码已编写为返回 BufferedImage,但是此时我愿意接受一维字符数组的返回,然后将其放入 BufferedImage。

顺便说一下...这里的问题是:将图像数据的一维字符数组复制到 BufferedImage 中的最有效方法是什么?

提供的是我用来从 OpenCV 获取图像并复制到 BufferedImage 中的(低效)代码:

JNIEXPORT jobject JNICALL Java_graphicanalyzer_ImageFeedOpenCV_getFrame
  (JNIEnv * env, jobject jThis, jobject camera)
{
 //get the memory address of the CvCapture device, the value of which is encapsulated in the camera jobject
 jclass cameraClass = env->FindClass("graphicanalyzer/Camera");
 jfieldID fid = env->GetFieldID(cameraClass,"pCvCapture","I");

 //get the address of the CvCapture device
 int a_pCvCapture = (int)env->GetIntField(camera, fid);

 //get a pointer to the CvCapture device
    CvCapture *capture = (CvCapture*)a_pCvCapture;

 //get a frame from the CvCapture device
 IplImage *frame = cvQueryFrame( capture );

 //get a handle on the BufferedImage class
 jclass bufferedImageClass = env->FindClass("java/awt/image/BufferedImage");
 if (bufferedImageClass == NULL)
 {
  return NULL;
 }

 //get a handle on the BufferedImage(int width, int height, int imageType) constructor
 jmethodID bufferedImageConstructor = env->GetMethodID(bufferedImageClass,"<init>","(III)V");

 //get the field ID of BufferedImage.TYPE_INT_RGB
 jfieldID imageTypeFieldID = env->GetStaticFieldID(bufferedImageClass,"TYPE_INT_RGB","I");

 //get the int value from the BufferedImage.TYPE_INT_RGB field
 jint imageTypeIntRGB = env->GetStaticIntField(bufferedImageClass,imageTypeFieldID);

 //create a new BufferedImage
 jobject ret = env->NewObject(bufferedImageClass, bufferedImageConstructor, (jint)frame->width, (jint)frame->height, imageTypeIntRGB);

 //get a handle on the method BufferedImage.getRaster()
 jmethodID getWritableRasterID = env->GetMethodID(bufferedImageClass, "getRaster", "()Ljava/awt/image/WritableRaster;");

 //call the BufferedImage.getRaster() method
 jobject writableRaster = env->CallObjectMethod(ret,getWritableRasterID);

 //get a handle on the WritableRaster class
 jclass writableRasterClass = env->FindClass("java/awt/image/WritableRaster");

 //get a handle on the WritableRaster.setPixel(int x, int y, int[] rgb) method
 jmethodID setPixelID = env->GetMethodID(writableRasterClass, "setPixel", "(II[I)V"); //void setPixel(int, int, int[])

 //iterate through the frame we got above and set each pixel within the WritableRaster
 jintArray rgbArray = env->NewIntArray(3);
 jint rgb[3];
 char *px;
 for (jint x=0; x < frame->width; x++)
 {
  for (jint y=0; y < frame->height; y++)
  {
   px = frame->imageData+(frame->widthStep*y+x*frame->nChannels);
   rgb[0] = abs(px[2]);  // OpenCV returns BGR bit order
   rgb[1] = abs(px[1]);  // OpenCV returns BGR bit order
   rgb[2] = abs(px[0]);  // OpenCV returns BGR bit order
   //copy jint array into jintArray
   env->SetIntArrayRegion(rgbArray,0,3,rgb); //take values in rgb and move to rgbArray
   //call setPixel()  this is a copy operation
   env->CallVoidMethod(writableRaster,setPixelID,x,y,rgbArray);
  }
 }

 return ret;  //return the BufferedImage
}

最佳答案

如果您希望让您的代码非常快并且仍然使用 Java,还有另一种选择。 AWT 窗口工具包有一个直接的 native 接口(interface),您可以使用它来使用 C 或 C++ 绘制到 AWT 表面。因此,无需将任何内容复制到 Java,因为您可以直接从 C 或 C++ 中的缓冲区进行渲染。我不确定如何执行此操作的具体细节,因为我有一段时间没有看过它,但我知道它包含在标准 JRE 发行版中。使用这种方法,如果您愿意,您可能会接近相机的 FPS 限制,而不是努力达到 30 FPS。

如果你想进一步研究这个,我会开始 herehere .

编程愉快!

关于java - 高效实现 Java native 接口(interface)网络摄像头源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4299449/

相关文章:

java - 给定 inDigits 的基础否定

java - postman 发送原始 json 不起作用

java - 如何使用另一场 war 的类文件

sql - 哪些索引有助于加快查询速度?

python-2.7 - 边缘检测图像中的对象识别?

java - 在android项目中使用apache poi写入excel文件

arrays - 从分类数组转换为二进制矩阵

performance - AVX中绝对值的浮点比较

image - 使用opencv仅去除图像中的黑点

python - 频域上的同态滤波(Python 和 OpenCV)