我的Opencv4Android添加V4L2支持的移植记录(3)

博主QQ:1356438802

本文实验平台:Eclipse + Opencv 2.4.10 + MTK Android 4.4平板(这一直是我的Android实验平台)

可能各位看官,看到前面的文章会觉得很凌乱,一会儿这个平台,一会儿那个平台。

其实我的主要思路就是:opencv中的任何一个功能,首先在windows上验证成功,再到Ubuntu,然后到Android上验证!

在windows上,由于其系统通用性,各方面支持肯定更好,所以我一定能验证成功,然后我再去Android上面做这些功能。这样前面的经验可以给我做参考,在Android上调试时可以敏锐滴知道问题出在哪里,并且这个过程下来三个平台都熟悉了,对于以后我的应用程序跨平台移植也是有帮助的!

回顾这段时间的工作,主要是三个阶段:

1. 在各个平台能够正常调用opencv的函数。例如yanzi_OpenCV4Android app的JNI里面有个灰度处理的函数Java_luo_uvc_jni_ImageProc_grayProc,就只是为了验证opencv可以调用。

2. 在各个平台能够正常获取图像,实现实时预览。这个阶段前面在windows、Ubuntu和Android基本都实现了,只是效果有差别而已。

3. 对2中的函数调用升级——在各个平台使用opencv2的C++类实现预览和录像。实际上opencv2的C++类只是对opencv1的C函数做了一次封装,但是看起来代码更干净简洁,也更方便和MAT结合。windows和Ubuntu已经验证完了,可以参考前面系列文章。

接下来我们验证VideoCapture和VideoWriter在Android上的使用!

把文章《我的Opencv4Android添加V4L2支持的移植记录(2)》中的例程yanzi_OpenCV4Android拷贝过来做如下修改

yanzi_OpenCV4Android6

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

#define NAMELEN	(64)

Mat frame;
VideoCapture capture;

char *prefix = NULL;
char fileName[NAMELEN] = {0};
VideoWriter writer;

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;

	if (capture.isOpened())
	{
		LOGE("camera is already opened!");
		return -2;
	}
	else
	{
		//打开一个默认的摄像头
		capture.open(-1);
		if (capture.isOpened())
		{
			LOGE("camera open success!");
			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 (capture.isOpened())
	{
		capture.release();
		LOGE("camera release success!");
		return 0;
	}

//	LOGI("usb camera Java_luo_uvc_jni_ImageProc_releaseCamera end ....\n");

	return -1;
}

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 (capture.isOpened())
	{
		LOGI("------------start capture frame\n");

		//取图片帧
		capture >> frame;
		if (frame.empty())
		{
			LOGE("capture frame empty!");
			return NULL;
		}

		//将图片写入视频文件
		if(writer.isOpened())
		{
			//录制视频用的frame不需要转格式
			writer.write(frame);
		}

		struct FrameInfoClass frameInfoClass;

		//内部类用$
		//luo/uvc/jni/ImageProc$FrameInfo
		jclass class2 = _env->FindClass("luo/uvc/jni/ImageProc$FrameInfo");

		frameInfoClass.width = _env->GetFieldID(class2, "mWidth", "I");

		frameInfoClass.heigth = _env->GetFieldID(class2, "mHeight", "I");

		frameInfoClass.imageSize = _env->GetFieldID(class2, "mImageSize", "I");

		frameInfoClass.pixels = _env->GetFieldID(class2, "mPixels", "[I");

		//
		jobject joFrame = _env->AllocObject(class2);

		LOGI("frame->cols = %d\n", frame.cols);
		LOGI("frame->rows = %d\n", frame.rows);
//		LOGI("frame->imageSize = %d\n", frame->imageSize);

		_env->SetIntField(joFrame, frameInfoClass.width, frame.cols);
		_env->SetIntField(joFrame, frameInfoClass.heigth, frame.rows);
//		_env->SetIntField(joFrame, frameInfoClass.imageSize, frame->imageSize);

		int size = frame.cols * frame.rows;
		//创建一个新的java数组(jarray),但是jarray不是C数组类型,不能直接访问jarray
		jintArray jiArr = _env->NewIntArray(size);
		jint *ji;

#if 1   //可用
		//RGB --> ARGB8888
		Mat frameARGB;
		cvtColor(frame, 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);

#else   //可用
		//可以使用GetIntArrayRegion函数来把一个 int数组中的所有元素复制到一个C缓冲区中
		//SetIntArrayRegion则是逆过程
		_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_startRecord
  (JNIEnv *env, jclass, jstring jstr)
{
	if(writer.isOpened() == false)
	{
		 prefix = jstringToChar(env, jstr);
		 memset(fileName, 0, NAMELEN);
		 sprintf(fileName, "/storage/sdcard0/Movies/%s.avi", prefix);
		 LOGI("fileName: %s", fileName);
		 FREE(prefix);

		 writer.open(fileName, CV_FOURCC('F', 'L', 'V', '1')/*有效*/, 30, cv::Size(640, 480),true);
//		 writer.open(fileName, CV_FOURCC('M', 'J', 'P', 'G')/*有效*/, 30, cv::Size(640, 480),true);
//		 writer.open(fileName, CV_FOURCC('D', 'I', 'V', 'X')/*有效*/, 30, cv::Size(640, 480),true);
//		 writer.open(fileName, CV_FOURCC('X', 'V', 'I', 'D')/*有效*/, 30, cv::Size(640, 480),true);
		 if(writer.isOpened())
		 {
			 LOGE("writer open successful!");
			 return 0;
		 }
		 else
		 {
			 LOGE("writer open failed!");
			 return -2;
		 }

	}
	else
	{
		//实际上已经在录像
		return -1;
	}
}

JNIEXPORT jint JNICALL Java_luo_uvc_jni_ImageProc_stopRecord
  (JNIEnv *, jclass)
{
	if (writer.isOpened())
	{
		//如果正在录像,则停止录像
		writer.release();
		LOGI("%s end record!", fileName);

		return 0;
	}

	return -1;
}

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;
}

//jstring to char*
char* jstringToChar(JNIEnv* env, jstring jstr)
{
	char* rtn = NULL;
	jclass clsstring = env->FindClass("java/lang/String");
	jstring strencode = env->NewStringUTF("utf-8");
	jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
	jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
	jsize alen = env->GetArrayLength(barr);
	jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);

	if (alen > 0)
	{
		rtn = (char*)calloc(1, alen + 1);
		memcpy(rtn, ba, alen);
		rtn[alen] = 0;
	}

	env->ReleaseByteArrayElements(barr, ba, 0);
	return rtn;
}

#ifdef __cplusplus
}
#endif
/* end of extern */

CameraPreview.java

package luo.uvc.jni;

import java.text.SimpleDateFormat;
import java.util.Date;

import luo.uvc.jni.ImageProc.FrameInfo;
import android.R.bool;
import android.R.string;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback, Runnable
{
	public static final String TAG = "UVCCameraPreview";
	protected Context context;

	private SurfaceHolder holder;
	Thread mainLoop = null;

	public static final int SET_PREVIEW_TEXT = 0;
	public static final int SET_RECORD_TEXT = 1;

	private boolean mIsOpened = false;
	private boolean mIsRecording = false;
	private boolean shouldStop = false;

	public callback textCallback;

	// The following variables are used to draw camera images.
	private int winWidth = 0;
	private int winHeight = 0;
	private Rect rect;
	private int dw, dh;
	private float rate;

	public CameraPreview(Context context)
	{
		super(context);
		// TODO Auto-generated constructor stub
		this.context = context;
		Log.d(TAG, "CameraPreview constructed");
		setFocusable(true);

		holder = getHolder();
		holder.addCallback(this);
		holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);

	}

	//注意:使用findViewById获取CameraPreview,会调用这个构造函数
	public CameraPreview(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		this.context = context;
		Log.d(TAG, "CameraPreview constructed");
		setFocusable(true);

		holder = getHolder();
		holder.addCallback(this);
		holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);

	}	

	public void initPreview()
	{
		int index = -1;
		if (mIsOpened == false)
		{
			if (0 == ImageProc.connectCamera(index))
			{
				Log.i(TAG, "open uvc success!!!");
				mIsOpened = true;
				textCallback.setViewText(SET_PREVIEW_TEXT, "关闭");
				if (null != mainLoop)
				{
					shouldStop = false;
					Log.i(TAG, "preview mainloop starting...");
					mainLoop.start();
				}

				Toast.makeText(context.getApplicationContext(), "成功打开摄像头", Toast.LENGTH_SHORT).show();
			} else
			{
				Log.i(TAG, "open uvc fail!!!");
				mIsOpened = false;
				Toast.makeText(context.getApplicationContext(), "摄像头打开失败", Toast.LENGTH_SHORT).show();
			}

		} else
		{
			uninitPreview();
		}
	}

	public void uninitPreview()
	{
		//结束录制
		uninitRecord();

		//停止预览线程
		if (null != mainLoop)
		{
			Log.i(TAG, mainLoop.isAlive() ? "mainloop is alive!" : "mainloop is not alive!");
			if (mainLoop.isAlive())
			{
				shouldStop = true;
				while (shouldStop)
				{
					try
					{
						Thread.sleep(100); // wait for thread stopping
					} catch (Exception e)
					{
					}
				}
			}
		}

		//关闭camera
		if (mIsOpened)
		{
			mIsOpened = false;
			ImageProc.releaseCamera();
			textCallback.setViewText(SET_PREVIEW_TEXT, "打开");
			Log.i(TAG, "release camera...");
		}
	}

	public void initRecord()
	{
		if(mIsOpened)
		{
			if(mIsRecording == false)
			{
				Log.i(TAG, "init camera record!");
				Date date = new Date();
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
				String dateString = simpleDateFormat.format(date);
				if(null == dateString)
				{
					dateString = "luoyouren";
				}
				Log.i(TAG, dateString);

				if(0 == ImageProc.startRecord(dateString))
				{
					mIsRecording = true;
					textCallback.setViewText(SET_RECORD_TEXT, "停止");
					Toast.makeText(context.getApplicationContext(), "开始录制...", Toast.LENGTH_SHORT).show();
				}
				else
				{
					mIsRecording = false;
					Log.e(TAG, "init camera record failed!");
					Toast.makeText(context.getApplicationContext(), "录制启动失败!", Toast.LENGTH_SHORT).show();
				}
				return;
			}
			else
			{
				uninitRecord();
				return;
			}
		}
		else
		{
			Log.e(TAG, "camera has not been opened!");
			return;
		}
	}

	public void uninitRecord()
	{
		if(mIsRecording)
		{
			Log.i(TAG, "camera is already recording! So we stop it.");
			ImageProc.stopRecord();
			mIsRecording = false;
			textCallback.setViewText(SET_RECORD_TEXT, "录制");
			return;
		}
	}

	public boolean isOpen()
	{
		return mIsOpened;
	}

	public boolean isRecording()
	{
		return mIsRecording;
	}

	@Override
	public void run()
	{
		// TODO Auto-generated method stub
		while (true && mIsOpened)
		{
			// get camera frame
			FrameInfo frame = ImageProc.getFrame();
			if (null == frame)
			{
				continue;
			}

			int w = frame.getWidth();
			int h = frame.getHeigth();
			Log.i(TAG, "frame.width = " + w + " frame.height = " + h);
			// 根据图像大小更新显示区域大小
			// 一般来说图像大小不会变化: 640x480
			updateRect(w, h);

			Bitmap resultImg = Bitmap.createBitmap(w, h, Config.ARGB_8888);

			resultImg.setPixels(frame.getPixels(), 0, w, 0, 0, w, h);

			// 刷surfaceview显示
			Canvas canvas = getHolder().lockCanvas();
			if (canvas != null)
			{
				// draw camera bmp on canvas
				canvas.drawBitmap(resultImg, null, rect, null);

				getHolder().unlockCanvasAndPost(canvas);
			}

			if (shouldStop)
			{
				shouldStop = false;
				Log.i(TAG, "mainloop will stop!");
				break;
			}
		}

		Log.i(TAG, "mainloop break while!");
	}

	public void updateRect(int frame_w, int frame_h)
	{
		// obtaining display area to draw a large image
		if (winWidth == 0)
		{
			winWidth = this.getWidth();
			winHeight = this.getHeight();

			if (winWidth * 3 / 4 <= winHeight)
			{
				dw = 0;
				dh = (winHeight - winWidth * 3 / 4) / 2;
				rate = ((float) winWidth) / frame_w;
				rect = new Rect(dw, dh, dw + winWidth - 1, dh + winWidth * 3 / 4 - 1);
			} else
			{
				dw = (winWidth - winHeight * 4 / 3) / 2;
				dh = 0;
				rate = ((float) winHeight) / frame_h;
				rect = new Rect(dw, dh, dw + winHeight * 4 / 3 - 1, dh + winHeight - 1);
			}
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceCreated(SurfaceHolder arg0)
	{
		// TODO Auto-generated method stub
		mainLoop = new Thread(this);

		updateRect(512, 512);
		// 将lena图像加载程序中并进行显示
		Bitmap resultImg = BitmapFactory.decodeResource(getResources(), R.drawable.lena);

		// 刷surfaceview显示
		Canvas canvas = getHolder().lockCanvas();
		if (canvas != null)
		{
			// draw camera bmp on canvas
			canvas.drawBitmap(resultImg, null, rect, null);

			getHolder().unlockCanvasAndPost(canvas);
		}

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder arg0)
	{
		// TODO Auto-generated method stub

	}

}

其实改动不是很大,ImageProc.cpp中CvCapture替换成VideoCapture,CameraPreview.java增加InitRecord和UninitRecord。

编译、安装、运行后,Camera实时预览正常,和《我的Opencv4Android添加V4L2支持的移植记录(2)》中一样,但是录像失败!

我们可以来看看opencv里面,对于各个平台VideoWriter功能的分发情况

/**
 * Videowriter dispatching method: it tries to find the first
 * API that can write a given stream.
 */
CV_IMPL CvVideoWriter* cvCreateVideoWriter( const char* filename, int fourcc,
                                            double fps, CvSize frameSize, int is_color )
{
    //CV_FUNCNAME( "cvCreateVideoWriter" );

    CvVideoWriter *result = 0;

#pragma message("------------------------ cvCreateVideoWriter")

    if(!fourcc || !fps)
        result = cvCreateVideoWriter_Images(filename);

#ifdef HAVE_FFMPEG
#pragma message("------------------------ HAVE_FFMPEG")
    if(!result)
        result = cvCreateVideoWriter_FFMPEG_proxy (filename, fourcc, fps, frameSize, is_color);
#endif

#ifdef HAVE_VFW
#pragma message("------------------------ HAVE_VFW")
    if(!result)
        result = cvCreateVideoWriter_VFW(filename, fourcc, fps, frameSize, is_color);
#endif

#ifdef HAVE_MSMF
#pragma message("------------------------ HAVE_MSMF")
    if (!result)
        result = cvCreateVideoWriter_MSMF(filename, fourcc, fps, frameSize, is_color);
#endif

/*  #ifdef HAVE_XINE
    if(!result)
        result = cvCreateVideoWriter_XINE(filename, fourcc, fps, frameSize, is_color);
    #endif
*/
#ifdef HAVE_AVFOUNDATION
#pragma message("------------------------ HAVE_AVFOUNDATION")
    if (! result)
        result = cvCreateVideoWriter_AVFoundation(filename, fourcc, fps, frameSize, is_color);
#endif

#if defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT)
#pragma message("------- defined(HAVE_QUICKTIME) || defined(HAVE_QTKIT)")
    if(!result)
        result = cvCreateVideoWriter_QT(filename, fourcc, fps, frameSize, is_color);
#endif

#ifdef HAVE_GSTREAMER
#pragma message("------------------------ HAVE_GSTREAMER")
    if (! result)
        result = cvCreateVideoWriter_GStreamer(filename, fourcc, fps, frameSize, is_color);
#endif

#if !defined(HAVE_FFMPEG) &&     !defined(HAVE_VFW) &&     !defined(HAVE_MSMF) &&     !defined(HAVE_AVFOUNDATION) &&     !defined(HAVE_QUICKTIME) &&     !defined(HAVE_QTKIT) &&     !defined(HAVE_GSTREAMER)
// If none of the writers is used
// these statements suppress 'unused parameter' warnings.
    (void)frameSize;
    (void)is_color;
#endif

#pragma message("------------------------ cvCreateVideoWriter_Images")
    if(!result)
        result = cvCreateVideoWriter_Images(filename);

    return result;
}

再对比下 下面的表

enum
{
    CV_CAP_ANY      =0,     // autodetect

    CV_CAP_MIL      =100,   // MIL proprietary drivers

    CV_CAP_VFW      =200,   // platform native
    CV_CAP_V4L      =200,
    CV_CAP_V4L2     =200,

    CV_CAP_FIREWARE =300,   // IEEE 1394 drivers
    CV_CAP_FIREWIRE =300,
    CV_CAP_IEEE1394 =300,
    CV_CAP_DC1394   =300,
    CV_CAP_CMU1394  =300,

    CV_CAP_STEREO   =400,   // TYZX proprietary drivers
    CV_CAP_TYZX     =400,
    CV_TYZX_LEFT    =400,
    CV_TYZX_RIGHT   =401,
    CV_TYZX_COLOR   =402,
    CV_TYZX_Z       =403,

    CV_CAP_QT       =500,   // QuickTime

    CV_CAP_UNICAP   =600,   // Unicap drivers

    CV_CAP_DSHOW    =700,   // DirectShow (via videoInput)
    CV_CAP_MSMF     =1400,  // Microsoft Media Foundation (via videoInput)

    CV_CAP_PVAPI    =800,   // PvAPI, Prosilica GigE SDK

    CV_CAP_OPENNI   =900,   // OpenNI (for Kinect)
    CV_CAP_OPENNI_ASUS =910,   // OpenNI (for Asus Xtion)

    CV_CAP_ANDROID  =1000,  // Android
    CV_CAP_ANDROID_BACK =CV_CAP_ANDROID+99, // Android back camera
    CV_CAP_ANDROID_FRONT =CV_CAP_ANDROID+98, // Android front camera

    CV_CAP_XIAPI    =1100,   // XIMEA Camera API

    CV_CAP_AVFOUNDATION = 1200,  // AVFoundation framework for iOS (OS X Lion will have the same API)

    CV_CAP_GIGANETIX = 1300,  // Smartek Giganetix GigEVisionSDK

    CV_CAP_INTELPERC = 1500 // Intel Perceptual Computing SDK
};

cvCreateVideoWriter在windows下可以用cvCreateVideoWriter_VFW,在Ubuntu下可以用cvCreateVideoWriter_FFMPEG_proxy,可以在Android平台它用啥?没得用

看看之前的Cmake的结果对比就知道:

Opencv在我增加V4L2后,也支持NativeCamera和V4L2两种Video I/O,而Ubuntu有FFMPEG的支持(不懂FFMPEG的去问度娘)。并且在cvCreateVideoWriter函数中也没有对NativeCamera的支持,所以我的APP录像失败。

所以如果要让yanzi_OpenCV4Android 能够录像,就要添加FFMPEG的支持!!!!

PS:

昨天我一直在纠结一个问题,VFW和DirectShow都是微软视频处理框架,DirectShow是VFW的升级版。但是在Opencv中,

cvCreateCameraCapture 读摄像头

cvCreateFileCapture 读视频文件

cvCreateVideoWriter 写视频文件

和摄像头相关的cvCreateCameraCapture
里面对DirectShow和VFW都有支持,但是和文件相关的后面两个函数只对VFW有支持。

我猜测大概是DirectShow不善于处理视频文件。

时间: 2024-08-01 10:49:48

我的Opencv4Android添加V4L2支持的移植记录(3)的相关文章

我的Opencv4Android添加V4L2支持的移植记录

博主QQ:1356438802 引言 前面几篇文章我已经在Windows / Ubuntu / Android 三个平台成功安装了opencv的使用/编程环境,但是我需要在android平台使用usb video  camera(即UVC).当然在Ubuntu上,默认编译出来的opencv库本身就支持V4L2(UVC的操作API函数集),而Windows是将UVC纳入了DirectShow框架,默认也是支持的:唯独对于Android平台,有且仅有对Android Native Camera的支持

s3c2440 移植linux内核 添加网卡支持 yaffs2文件系统支持

三.内核的移植 说明:针对的是百问网的jz2440 gcc:4.9.1 1.移植内核 首先,下载源码包:https://www.kernel.org/ 现在时间为2014年12月20日其主界面截图为: 在此,就在下较新的稳定的版本作为尝试.在这里有个命名问题需要说明一下,最前面的2.6或者3.18是主版本号码,后面的次版本号是比如2.6.32.65中的32,再后面的比如2.6.32.65中的65是升级版本号,主版本号为奇数的是开发本比如3.17,主版本号为偶数的为稳定版比如3.18,一般情况下名

为UiAutomatorViewer添加xpath支持

UiAutomatorViewer是Android SDK自带的测试工具,用来查看手机或模拟器上的界面元素,小巧,简单,开箱即用,十分方便.美中不足之处在于,它不能获取界面元素的xpath. 写自动化测试脚本时,xpath是一种非常方便的定位方式.Appium等一些成熟的工具框架可以获取到界面元素xpath,但使用起来稍有点重量级.那么是否也可以给UiAutomatorViewer添加xpath支持呢? 答案是肯定的. 首先下载UiAutomatorView源代码,我用的地址是https://a

为Gradle添加UTF-8支持

gradle默认使用系统字符编码,大多数中文系统是使用GBK编码 但程序员绝大部分都是使用UTF-8写各类java文件以及其他资源文件 编译时很容易报错,比如下面的错误: ”警告:编码 GBK 的不可映射字符“ 有两种办法可以给gradle添加UTF-8支持 第一种,在我们的项目配置文件build.gradle中最后添加下面的语句 tasks.withType(JavaCompile) { options.encoding = "UTF-8" } 这样,这个项目在进行gradle打包时

为UIAlertView添加block支持

系统自带的UIAlertView只能支持delegate方式. 如果你只有一个UIAlertView这种方式可能无关紧要. 但如果你有二个或多个UIAlertView, 你需要在委托方法中进行判断是哪个UIAlertView实例的产生的委托, 接着又要判断是响应哪个button. 如果你曾经这样做过, 想想这是多杂的代码. Objective-C是支持块代码的, 如果对UIAlertView添加块支持, 那岂不是一个美事. 这里推荐一个开源的实现: https://github.com/jiv

Ubuntu下安装VirtualBox并为其添加USB支持

1.下载VirtualBox软件包和USB支持包 下载网址均为为:https://www.virtualbox.org/wiki/Downloads VirtualBox软件包下载项: VirtualBox 4.3.10 for Linux hosts USB支持包下载项: VirtualBox 4.3.10 Oracle VM VirtualBox Extension Pack All supported platforms 2.下载完成后,进行安装 3.添加当前用户到组 sudo gedit

Qt国际化(Q_DECLARE_TR_FUNCTIONS() 宏给非Qt类添加翻译支持,以前没见过QTextEncoder和QTextDecoder和QLibraryInfo::location()和QEvent::LanguageChange)

Internationalization with Qt 应用程序的国际化就是使得程序能在国际间可用而不仅仅是在本国可用的过程. Relevant Qt Classes andAPIs 以下的类支持Qt的国际化. QTextCodec QTextDecoder QTextEncoder QTranslator QLocale Languages and WritingSystems 有时,国际化是比较简单的,例如,把美国的应用程序让澳大利亚或英国的用户可访问,只需要简单的改变拼写.但是,把美国的

在IntelliJ IDEA中添加框架支持时找不到Hibernate的解决办法

问题描述 第一次在Add Frameworks support界面中添加hibernate支持的时候,异常中断,导致没有成功添加. 第二次进入Add Frameworks support窗口时,发现找不到hibernate. 解决办法 打开项目根目录下的spring-mvc-crud.iml文件,搜索hibernate找到这段代码并删除,然后重新添加框架支持即可选择hibernate. <facet type="hibernate" name="Hibernate&qu

为Vim 添加vimgdb支持

为Vim 添加vimgdb支持 1.    下载最新的vim74的源码包 wget ftp://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz2 2.下载vimgdb-for-vim7.4源码 wget https://github.com/larrupingpig/vimgdb-for-vim7.4/archive/master.zip -O vimgdb-for-vim74.zip 3.解压所有文件,并应用补丁文件 tar xjvf vim-7.4.tar.b