博主QQ:1356438802
QQ群:473383394——UVC&OpenCV47
这次我们来对《我的Opencv4Android添加V4L2支持的移植记录(2)》的实时预览进行一些优化。
先来看那篇文章中源码的ImageProc.cpp文件
#include "ImageProc.h"
#include "cv.h"
#include "highgui.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <string>
#include <vector>
using namespace cv;
using namespace std;
#ifdef __cplusplus
extern "C" {
#pragma message("------------------------ ImageProc.cpp")
#endif
CvCapture *pCapture = NULL;
IplImage *frame = NULL;
int id;
JNIEXPORT jintArray JNICALL Java_luo_uvc_jni_ImageProc_grayProc(JNIEnv* env,
jclass obj, jintArray buf, jint w, jint h)
{
jint *cbuf;
cbuf = env->GetIntArrayElements(buf, false);
if (cbuf == NULL)
{
return 0;
}
Mat imgData(h, w, CV_8UC4, (unsigned char*) cbuf);
uchar* ptr = imgData.ptr(0);
for (int i = 0; i < w * h; i++)
{
//计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
//对于一个int四字节,其彩色值存储方式为:BGRA
int grayScale = (int) (ptr[4 * i + 2] * 0.299 + ptr[4 * i + 1] * 0.587
+ ptr[4 * i + 0] * 0.114);
ptr[4 * i + 1] = grayScale;
ptr[4 * i + 2] = grayScale;
ptr[4 * i + 0] = grayScale;
}
int size = w * h;
jintArray result = env->NewIntArray(size);
env->SetIntArrayRegion(result, 0, size, cbuf);
env->ReleaseIntArrayElements(buf, cbuf, 0);
return result;
}
JNIEXPORT jint JNICALL Java_luo_uvc_jni_ImageProc_connectCamera(JNIEnv* env,
jclass obj, jint device)
{
id = device;
pCapture = NULL;
LOGI("usb camera id=%d Java_luo_uvc_jni_ImageProc_connectCamera start ....\n", id);
//查找UVC设备
pCapture = cvCaptureFromCAM(id);
if (NULL != pCapture)
{
LOGE("usb camera open success!\n");
return 0;
}
LOGI("usb camera Java_luo_uvc_jni_ImageProc_connectCamera end ....\n");
return -1;
}
JNIEXPORT jint JNICALL Java_luo_uvc_jni_ImageProc_releaseCamera(JNIEnv* env,
jclass obj)
{
LOGI("usb camera Java_luo_uvc_jni_ImageProc_releaseCamera start ....\n");
if (NULL != pCapture)
{
cvReleaseCapture(&pCapture);
pCapture = NULL;
}
LOGI("usb camera Java_luo_uvc_jni_ImageProc_releaseCamera end ....\n");
return 0;
}
struct FrameInfoClass
{
jfieldID width;
jfieldID heigth;
jfieldID imageSize;
jfieldID pixels;
};
JNIEXPORT jobject JNICALL Java_luo_uvc_jni_ImageProc_getFrame(JNIEnv* _env,
jclass obj)
{
LOGI("------------Java_luo_uvc_jni_ImageProc_getFrame\n");
if(NULL != pCapture)
{
LOGI("------------start cvQueryFrame\n");
frame = NULL;
frame = cvQueryFrame(pCapture); //取图片帧
if(NULL == frame)
{
LOGE("cvQueryFrame(capture) failed!!!!!!!!!!!!!!!!!!!!!!!");
return NULL;
}
struct FrameInfoClass frameInfoClass;
//内部类用$
//luo/uvc/jni/ImageProc$FrameInfo
jclass class2 = _env->FindClass("luo/uvc/jni/ImageProc$FrameInfo");
LOGI("1=GetFieldID\n");
frameInfoClass.width = _env->GetFieldID(class2, "mWidth", "I");
LOGI("2=GetFieldID\n");
frameInfoClass.heigth = _env->GetFieldID(class2, "mHeight", "I");
LOGI("3=GetFieldID\n");
frameInfoClass.imageSize = _env->GetFieldID(class2, "mImageSize", "I");
LOGI("4=GetFieldID\n");
frameInfoClass.pixels = _env->GetFieldID(class2, "mPixels", "[I");
LOGI("5=GetFieldID\n");
//
jobject joFrame = _env->AllocObject(class2);
LOGI("6=GetFieldID\n");
LOGI("frame->width = %d\n", frame->width);
LOGI("frame->height = %d\n", frame->height);
LOGI("frame->imageSize = %d\n", frame->imageSize);
_env->SetIntField(joFrame, frameInfoClass.width, frame->width);
_env->SetIntField(joFrame, frameInfoClass.heigth, frame->height);
_env->SetIntField(joFrame, frameInfoClass.imageSize, frame->imageSize);
int size = frame->width * frame->height;
//创建一个新的java数组(jarray),但是jarray不是C数组类型,不能直接访问jarray
jintArray jiArr = _env->NewIntArray(size);
jint *ji;
#if 1 //可用
LOGI("7=GetFieldID\n");
//RGB --> ARGB8888
Mat frameRGB(frame->height, frame->width, CV_8UC3, frame->imageData), frameARGB;
cvtColor(frameRGB, frameARGB, CV_RGB2RGBA);
//JNI支持一系列的Get/Release<Type>ArrayElement 函数,允许本地代码获取一个指向基本C类型数组的元素的指针。
ji = _env->GetIntArrayElements(jiArr, 0);
memcpy((jbyte *)ji, frameARGB.data, 4 * size);
_env->ReleaseIntArrayElements(jiArr, ji, 0); //可加,可不加
_env->SetObjectField(joFrame, frameInfoClass.pixels, jiArr);
LOGI("8=GetFieldID\n");
#else //可用
//可以使用GetIntArrayRegion函数来把一个 int数组中的所有元素复制到一个C缓冲区中
//SetIntArrayRegion则是逆过程
LOGI("13=GetFieldID\n");
_env->SetIntArrayRegion(jiArr, 0, 2, abc);
_env->SetObjectField(joFrame, company_class.money, jiArr);
#endif
LOGI("Java_luo_uvc_jni_ImageProc_getFrame end\n");
return joFrame;
}
LOGI("=================Java_luo_uvc_jni_ImageProc_getFrame failed\n");
return 0;
}
JNIEXPORT jint JNICALL Java_luo_uvc_jni_ImageProc_getWidth(JNIEnv* env,
jclass obj)
{
return 0;
}
JNIEXPORT jint JNICALL Java_luo_uvc_jni_ImageProc_getHeight(JNIEnv* env,
jclass obj)
{
return 0;
}
#ifdef __cplusplus
}
#endif
/* end of extern */
其中Java_luo_uvc_jni_ImageProc_getFrame这个函数是提取图像,给SurfaceView刷新显示的,由于它整个流程操作数据块比较多,而且在JNI层构造了一个FrameInfo对象,效率很低。我们对其进行改造。
void Java_luo_uvc_jni_ImageProc_getFrame(JNIEnv* env, jobject thiz,
jobject bitmap)
{
AndroidBitmapInfo info;
void *pixels;
char *pixel;
int ret;
int width = 0;
int height = 0;
int ImageSize = 0;
if (NULL != pCapture)
{
//1. get frame
LOGI("------------start cvQueryFrame\n");
frame = NULL;
frame = cvQueryFrame(pCapture);
if (NULL == frame)
{
LOGE("cvQueryFrame(capture) failed!!!!!!!!!!!!!!!!!!!!!!!");
return;
}
if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
{
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return;
}
width = info.width;
height = info.height;
ImageSize = width * height;
LOGI("------------info.width=%d, info.height=%d\n", info.width,
info.height);
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
{
LOGE("Bitmap format is not RGBA_8888 !");
return;
}
if ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixels)) < 0)
{
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return;
}
pixel = (char *)pixels;
for (int i = 0; i < ImageSize; i++)
{
//需要注意的是对于ARGB_8888类型的图像而言,其每一个像素值在int型数据中的存储序列为BGRA。
*(pixel + 4 * i + 0) = *(frame->imageData + 3 * i + 2);
*(pixel + 4 * i + 1) = *(frame->imageData + 3 * i + 1);
*(pixel + 4 * i + 2) = *(frame->imageData + 3 * i + 0);
*(pixel + 4 * i + 3) = 0xFF;
}
AndroidBitmap_unlockPixels(env, bitmap);
}
}
其实就是取java层的Bitmap对象的data空间,直接把frame-->imageData拷贝过去。特别注意这里的RGB-->ARGB_8888格式转换方式,上面是用MAT矩阵转换,而我这里采取直接复制的方式,本质是一样的。 对于ARGB_8888类型的图像而言,其每一个像素值在int型数据中的存储序列为BGRA。
经过这样的修改后,预览的实时性有很大的提高!
示例下载:http://download.csdn.net/detail/luoyouren/9594703