Я работаю над проектом, который берет видеовход с веб-камеры и отображает области движения для пользователя. Моя попытка "бета" в этом проекте состояла в том, чтобы использовать Java Media Framework для извлечения веб-камеры. Благодаря некоторым функциям утилиты JMF удобно возвращает фреймы веб-камер в качестве BufferedImages, и я создал значительную часть структуры для обработки. Однако вскоре я понял, что JMF не поддерживается Sun/Oracle больше, а некоторые из более высоких разрешений веб-камеры (720p) недоступны через интерфейс JMF.
Я хотел бы продолжить обработку фреймов как BufferedImages и использовать OpenCV (С++) для источника видеопотока. Используя только OpenCV-структуру, я обнаружил, что OpenCV отлично справляется с эффективным возвратом фреймов веб-камер высокого разрешения и рисует их на экране.
Я полагал, что было бы довольно просто передать эти данные на Java и добиться такой же эффективности. Я только что закончил писать JNI DLL, чтобы скопировать эти данные в BufferedImage и вернуть его на Java. Тем не менее, я нахожу, что количество копий данных, которые я делаю, действительно затрудняет работу. Я нацелен на 30 FPS, но для копирования копий данных из массива char, возвращаемого OpenCV, в Java BufferedImage требуется примерно 100 мс. Вместо этого я вижу около 2-5 FPS.
При возврате захвата кадра OpenCV предоставляет указатель на массив 1D char. Эти данные должны быть предоставлены Java, и, видимо, у меня нет времени на их копирование.
Мне нужно лучшее решение для захвата этих кадров в BufferedImage. Несколько решений, которые я рассматриваю, ни один из которых, я думаю, не очень хорош (достаточно уверен, что они также будут работать плохо):
(1) Переопределите BufferedImage и верните данные пикселей из различных методов BufferedImage, выполнив собственные вызовы в DLL. (Вместо того, чтобы делать копирование массива сразу, я возвращаю отдельные пиксели по запросу вызывающего кода). Обратите внимание, что для вызова кода обычно требуется все пиксели изображения, чтобы нарисовать изображение или обработать его, поэтому эта индивидуальная операция захвата пикселей будет реализована в 2D-контуре.
(2) Попросите BufferedImage использовать java.nio.ByteBuffer для прямого доступа к данным в массиве char, возвращаемом OpenCV. Поблагодарили бы за какие-либо советы относительно того, как это делается.
(3) Делайте все на С++ и забывайте Java. Хорошо, да, это звучит как наиболее логичное решение, однако у меня не будет времени, чтобы начать этот многомесячный проект с нуля.
На данный момент мой код JNI был написан для возврата BufferedImage, однако на данный момент я готов принять возврат массива 1D char, а затем поместить его в BufferedImage.
Кстати... вопрос здесь: Каков наиболее эффективный способ копирования массива данных изображения 1D char в 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
}