Эффективная реализация веб-камеры для собственного интерфейса Java

Я работаю над проектом, который принимает видеовход с веб-камеры и отображает области движения для пользователя. Моя "бета" попытка в этом проекте заключалась в использовании Java Media Framework для получения потока веб-камеры. С помощью некоторых служебных функций JMF удобно возвращает кадры веб-камеры как BufferedImages, для обработки которых я построил значительный объем фреймворка. Однако вскоре я понял, что JMF больше не поддерживается Sun / Oracle, а некоторые из более высоких разрешений веб-камеры (720p) недоступны через интерфейс JMF.

Я хотел бы продолжить обработку кадров как BufferedImages, и используйте OpenCV (C ++) для создания видеопотока. Используя только фреймворк OpenCV, я обнаружил, что OpenCV эффективно возвращает кадры веб-камеры высокого разрешения и отображает их на экране.

Я подумал, что было бы довольно просто передать эти данные в Java и добиться такой же эффективности . Я только что закончил писать JNI DLL, чтобы скопировать эти данные в BufferedImage и вернуть их в Java. Однако я обнаружил, что объем копируемых данных действительно снижает производительность. Я нацелен на 30 кадров в секунду, но только для копирования данных из массива символов, возвращаемого OpenCV, в Java BufferedImage требуется около 100 мс. Вместо этого я вижу около 2-5 кадров в секунду.

При возврате захвата кадра OpenCV предоставляет указатель на массив одномерных символов. Эти данные должны быть предоставлены Java, и, очевидно, у меня нет времени копировать их.

Мне нужно лучшее решение, чтобы получить эти захваченные кадры в BufferedImage. Я рассматриваю несколько решений, ни одно из которых, на мой взгляд, не является очень хорошим (почти уверен, что они также будут работать плохо):

(1) Переопределить BufferedImage, и возвращать данные пикселей из различных методов BufferedImage, выполняя собственные вызовы DLL. (Вместо одновременного копирования массива я возвращаю отдельные пиксели по запросу вызывающего кода). Обратите внимание, что вызывающему коду обычно требуются все пиксели изображения, чтобы нарисовать изображение или обработать его, поэтому эта отдельная операция захвата пикселей будет реализована в двухмерном цикле for.

(2) Укажите BufferedImage использовать java. nio.ByteBuffer для прямого доступа к данным в массиве символов, возвращаемом OpenCV. Буду признателен за любые советы, как это делается.

(3) Делайте все на C ++ и забудьте про Java. Что ж, да, это звучит как наиболее логичное решение, однако у меня не будет времени начинать этот многомесячный проект с нуля.

На данный момент мой код JNI был написан для возврата BufferedImage, но на данный момент точка I ' m готов принять возвращаемый массив одномерных символов и затем поместить его в 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
}
10
задан Tim Cooper 24 September 2011 в 12:58
поделиться