我正在从一个摄像头获取视频输入并向用户显示运动区域的项目。我在这个项目上的“测试”尝试是使用Java媒体框架来检索网络摄像头源。通过一些实用功能,JMF可以方便地将网络摄像头帧作为BufferedImages返回,从而构建了大量的框架进行处理。但是,我很快意识到JMF不再受Sun/Oracle的支持,并且一些较高的网络摄像头分辨率(720p)无法通过JMF接口访问。高效地实现Java本地接口摄像头馈送
我想继续处理帧为BufferedImages,并使用OpenCV(C++)来源视频饲料。单独使用OpenCV的框架,我发现OpenCV在高效率地返回高清摄像头帧并将它们绘制到屏幕上做得很好。
我认为将这些数据提供给Java并达到相同的效率相当简单。我刚写完JNI DLL将这些数据复制到BufferedImage并将其返回给Java。但是,我发现我正在执行的数据复制量实际上阻碍了性能。我的目标是30 FPS,但单独需要大约100毫秒才能将OpenCV返回的字符数组中的数据复制到Java BufferedImage中。相反,我看到2-5 FPS。
当返回帧捕获时,OpenCV提供了一个指向1D char数组的指针。这些数据需要提供给Java,显然我没有时间复制它。
我需要一个更好的解决方案来将这些帧捕获到一个BufferedImage中。我正在考虑一些解决方案,我认为这些解决方案都不是很好(相当肯定它们的性能还不错):
(1)重写BufferedImage,并通过调用本地调用来从各种BufferedImage方法返回像素数据DLL。 (而不是立即进行数组复制,我根据调用代码的要求返回单个像素)。请注意,调用代码通常需要图像中的所有像素来绘制图像或对其进行处理,因此这个单独的像素抓取操作将在2D for-loop中实现。
(2)指示BufferedImage使用java.nio.ByteBuffer以某种方式直接访问由OpenCV返回的char数组中的数据。希望有关如何完成这些任何提示。
(3)用C++做所有事情,忘记Java。好吧,是的,这听起来像是最合乎逻辑的解决方案,但我没有时间从头开始这个为期多月的项目。
截至目前,我的JNI代码已被写入以返回BufferedImage,但此时我愿意接受1D char数组的返回,然后将其放入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
}
虽然看起来像是在获取CvCapture设备地址和创建BufferedImage方面存在重大开销,但逐行执行的代码执行时间证明for循环需要将近100毫秒,而其余的代码在<5毫秒内执行。 – Jason 2010-11-28 22:20:18