2016-05-01 23:44:41 Revival_Liang 阅读数 359
  • Android底层技术:HAL驱动开发

    本课程提供开发者学习Android底层的HAL(硬件抽象层)的开发方法和技术。HAL所在的位置是介于Android系统服务与Linux内核之间,HAL Driver是以library形式出现,给HAL Stub调用,供Android System架构者调用。而HAL Stub则是google设计出来的,保护硬件厂商的硬件驱动。

    17825 人正在学习 去看看 高煥堂

以下为代码块:


package com.example.demo;



import java.io.File;


import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Media;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;


public class MainActivity extends Activity {


public static final int TAKE_PHOTO = 1;
public static final int CROP_PHOTO = 2;
public static final int CHOOSE_PH = 3;


Button b, chooseFromAlbum;
ImageView pictrue;
Uri imageUri;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b = (Button) findViewById(R.id.button1);
chooseFromAlbum = (Button) findViewById(R.id.choose_from_album);
pictrue = (ImageView) findViewById(R.id.imageView1);
chooseFromAlbum.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent,CHOOSE_PH);//打开相册。
}
});
b.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View v) {
File outputImage = new File(Environment
.getExternalStorageDirectory(), "output_image.jpg");// Environment.getExternalStorageDirectory()获取SD卡的根目录.
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();


} catch (Exception e) {
// TODO: handle exception
}
imageUri = Uri.fromFile(outputImage);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);// 个人理解拍完放在imageUri下。
startActivityForResult(intent, TAKE_PHOTO);// 启动相机程序。
}
});
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {// requestCode:拍完照传回1,裁剪完传回2。
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("scale", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);// 剪完放在放在imageUri下。
startActivityForResult(intent, CROP_PHOTO);// 启动裁剪程序。
} catch (Exception e) {
Log.i("test", "e=" + e);
}
}
break;
case CROP_PHOTO:
if (resultCode == RESULT_OK) {
try {
Bitmap bitMap = BitmapFactory
.decodeStream(getContentResolver().openInputStream(
imageUri));// 解码。
Log.i("test", "bitMap=" + bitMap);
pictrue.setImageBitmap(bitMap);// 将裁减的图片显示出来。
} catch (Exception e) {
Log.i("test", "e1=" + e);
}
}
break;
case CHOOSE_PH:
if(resultCode == RESULT_OK ){
//判断当前系统版本。
if(Build.VERSION.SDK_INT>=19){
// 4.4以上的系统版本使用这个方法处理。
handleImageOnKitKat(data);
}else{
// 4.4以下的系统版本使用这个方法处理。
handleImageBeforeKitKat(data);
}
}
break;
default:
break;
}
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data){
String imagePath = null;
Uri uri = data.getData();
Log.i("test","4.4以上uri="+uri);
if(DocumentsContract.isDocumentUri(this, uri)){
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())){
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID+"="+id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
}else if("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(contentUri,null);

}
}else if("content".equalsIgnoreCase(uri.getScheme())){
imagePath = getImagePath(uri,null);
}
//imagePath此时的路径为真实的手机图片的路径。
displayImage(imagePath);
}


private void handleImageBeforeKitKat(Intent data){
Uri uri = data.getData();
String imagePath = getImagePath(uri,null);
//imagePath此时的路径为真实的手机图片的路径。
displayImage(imagePath);
}
private String getImagePath(Uri uri,String selection){
String path = null;
// getContentResolver:获得内容解析;query:查询。
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
if(cursor!=null){
if(cursor.moveToFirst()){
// getColumnIndex:获得列索引。
path = cursor.getString(cursor.getColumnIndex(Media.DATA));
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath){
if(imagePath!=null){
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
pictrue.setImageBitmap(bitmap);
}else{
Toast.makeText(this, "错误!", Toast.LENGTH_LONG).show();
}
}


}

以下为AndroidManifest.xml文件内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.demo"
    android:versionCode="1"
    android:versionName="1.0" >


    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />
    <uses-permission android:name="androd.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>


</manifest>

以下为简单的布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.choosepictest.MainActivity" >


    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="打开相机" />


    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button1"
        android:layout_below="@+id/button1"
        android:layout_marginTop="131dp" />


    <Button
        android:id="@+id/choose_from_album"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button1"
        android:layout_below="@+id/button1"
        android:layout_marginTop="26dp"
        android:text="打开相册" />


</RelativeLayout>

2016-08-10 13:09:44 weiqiang_1989 阅读数 494
  • Android底层技术:HAL驱动开发

    本课程提供开发者学习Android底层的HAL(硬件抽象层)的开发方法和技术。HAL所在的位置是介于Android系统服务与Linux内核之间,HAL Driver是以library形式出现,给HAL Stub调用,供Android System架构者调用。而HAL Stub则是google设计出来的,保护硬件厂商的硬件驱动。

    17825 人正在学习 去看看 高煥堂

完整代码已上传github,https://github.com/54wall/LibgdxAndroidCamera

开发背景

简单介绍下自己,目前在人脸识别从事android开发与测试,公司的产品是人脸识别SDK,成品就是各种终端上的人脸识别设备,总监大人希望最好能有一个跨平台的开发框架,能够以一种语言为主,输出各种app。于是他找到了libGDX,一个多平台开发游戏的框架, 语言为java。libGDX是一个游戏开发工具,确切的说也就是一个java框架,按他的套路进行编写即可。
开始稍微看看游戏,有些误入歧途,然后他说要看看怎么掉摄像头。然后我又误入歧途的寻找寻找了一些有意思的东西:一个是抓幽灵的游戏ChaseWhisplyProject,来自 https://github.com/tvbarthel/ChaseWhisplyProject
ChaseWhisplyProject

另外一个beyondar,来自 https://github.com/BeyondAR/beyondar

beyondar

当然,上边两个多事在Android内实现的,而非在libgdx中实现,感谢Github,在libGDX的介绍中,有达人分享了他使用libGDX调用android摄像头的实例,他在libGDX中调用摄像头的目的简单来说就是AR,使用者可以通过APP看到设备背后的路面,或者和真实世界有交互,就像PokemonGO一样,当然全部的这些也需要算法支持。但是时间是2013年,我拷贝下来,进行了微调,发现是可以实现的,鉴于目前网络上还真没有许多的实例说明如何在LibGDX中调用android摄像头,我搜索libgdx camera 大部分给我的结果都是libgdx中的镜头,就是跟随演员的镜头,而不是设备摄像头,这里也对这个实例进行一个记录和补充,以及简单的实现。

wiki地址:https://github.com/libgdx/libgdx/wiki/Integrating-libgdx-and-the-device-camera

下面我也是基于这篇文章进行翻译,顺序当然有所改变,还有原代码中有包含一个类似初始页面的SplashActivity的我没有实现,目前还没有去了解libGDX的三维编程,代码的源码已上传至GitHub,再好的文字也比不上源码。
完整代码已上传github,https://github.com/54wall/LibgdxAndroidCamera
libGDX中到底是如何实现调用摄像头

很简单,就是在一个透明的画布上绘制libGDX的舞台演员,在画布的后边放置摄像头的预览画面。而文章主要解决的问题就是如何显示摄像头预览画面,和如何调用摄像头。

如何显示摄像头预览画面

我们知道,libGDX的代码主要全部在core项目中,其他各个平台的代码都是经过一个简单的代码启动启动然后,剩下的就去调用core项目中的代码,首先在libGDX初始化时,要进行一定的设置,才能让摄像头的预览画面显示在libGDX框架生成的app中,进入android项目组,修改AndroidLauncher.class代码如下

   AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
    cfg.r = 8;
    cfg.g = 8;
    cfg.b = 8;
    cfg.a = 8;    
    DeviceCameraControl cameraControl = new AndroidDeviceCameraController(this);
    initialize(new MyGdxGame0606(cameraControl), cfg);

你可以点击进入AndroidApplicationConfiguration去看看r,g,b,a原来数值如下

/** number of bits per color channel **/
public int r = 5, g = 6, b = 5, a = 0;

rgba的具体值指的是每种颜色的比特位数,这里改为8位,大概的意思就是每种颜色深度为8位,也就是2的8次幂,也就是256,和photoshop中意义一样。这么设置的原因是为了libGDX中的画布的后边能够正常的显示android的摄像头,不然颜色和在android上调用摄像头有出入,色彩不会同样的丰富。

同样在AndroidLauncher.class中,将OpenGL surface 模式设置成TRANSLUCENT

if (graphics.getView() instanceof SurfaceView) {
    SurfaceView glView = (SurfaceView) graphics.getView();
    // force alpha channel - I'm not sure we need this as the GL surface
    // is already using alpha channel                
    glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}

然后是,在AndroidLauncher.class新建一个post方法,用来帮助唤起一些异步线程

public void post(Runnable r) {
    handler.post(r);
}

以上就是AndroidLauncher.class所要完成的全部内容。
如何让屏幕透明

很简单,在core项目中MyGdxGame0606.class主类中的render()渲染方法中,要注意使用glClearColor()清屏时,参数的选择必须全部是0,这样相机预览的画面才会显示在render画面的后边
Gdx.gl20.glClearColor(0f, 0.0f, 0.0f, 0.0f);

清屏准备完毕,现在就是要调用android摄像头的时候到了

首先了解一下,把大象装冰箱中总共分几步?对于调用android摄像头的步骤则可以分如下几部?Android相机有特别的工作顺序必须遵守,通过application的callbacks回调函数能管理相机的状态,机器的工作状态将由AndroidDeviceCameraController.class进行管理(desktop没有实现,Android已实现)。
相机的工作状态一次是 Ready -> Preview -> autoFocusing -> ShutterCalled -> Raw PictureData -> Postview PictureData -> Jpeg PictureData -> Ready (可以做一个表格)
本实例代码实现的顺序也可以概括为 Ready -> Preview -> autoFocusing -> Jpeg PictureData -> Ready
新建AndroidDeviceCameraController

在core项目中,新建一个DeviceCameraController类,而为了配合core中DeviceCameraControl,在android项目中,新建一个AndroidDeviceCameraController 类,来控制设备的摄像头,它要继承DeviceCameraControl,同时还要实现Camera.PictureCallback:(android.hardware.Camera.PictureCallback)Camera.AutoFocusCallback(android.hardware.Camera.AutoFocusCallback),共计三个接口,来实现android摄像头从准备到拍摄的过程。

public class AndroidDeviceCameraController implements DeviceCameraControl, Camera.PictureCallback, Camera.AutoFocusCallback {
}

AndroidDeviceCameraController 新建后,逐步实现摄像头该有的各个功能。
1.准备显示预览信息的CameraSurface

我们产生一个CameraSurface类来负责管理摄像头和它收集的图像,这里我和android摄像相关的代码一致

    public class CameraSurface extends SurfaceView implements SurfaceHolder.Callback {
        private Camera camera;
public CameraSurface( Context context ) {
            super( context );
            // We're implementing the Callback interface and want to get notified
            // about certain surface events.
            getHolder().addCallback( this );
            // We're changing the surface to a PUSH surface, meaning we're receiving
            // all buffer data from another component - the camera, in this case.
            getHolder().setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS );
        }
public void surfaceCreated( SurfaceHolder holder ) {
            // Once the surface is created, simply open a handle to the camera hardware.
            camera = Camera.open();
        }
public void surfaceChanged( SurfaceHolder holder, int format, int width, int height ) {
            // This method is called when the surface changes, e.g. when it's size is set.
            // We use the opportunity to initialize the camera preview display dimensions.
            Camera.Parameters p = camera.getParameters();
            p.setPreviewSize( width, height );
            camera.setParameters( p );
// We also assign the preview display to this surface...
            try {
                camera.setPreviewDisplay( holder );
            } catch( IOException e ) {
                e.printStackTrace();
            }
        }
public void surfaceDestroyed( SurfaceHolder holder ) {
            // Once the surface gets destroyed, we stop the preview mode and release
            // the whole camera since we no longer need it.
            camera.stopPreview();
            camera.release();
            camera = null;
        }
public Camera getCamera() {
            return camera;
        }
    }

2.在android项目中增加相机预览视图

在android项目的AndroidDeviceController类,使用activity.addContentView,直接将cameraSurface显示在android设备的屏幕上

    @Override
    public void prepareCamera() {
        if (cameraSurface == null) {
            cameraSurface = new CameraSurface(activity);
        }
        activity.addContentView( cameraSurface, new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ) );
    }
@Override
public synchronized void prepareCamera() {
    if (cameraSurface == null) {
        cameraSurface = new CameraSurface(activity);
    }
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams  (680,680);
    params.rightMargin=150;//可以通过设置rightMargin控制组件的实际位置
    params.leftMargin=200;//可以通过设置rightMargin控制组件的实际位置
    params.topMargin=100;
    activity.addContentView(cameraSurface, params);
     

}

prepareCamera方法应该在libgdx渲染过程中异步调用

    @Override
    public void prepareCameraAsync() {
        Runnable r = new Runnable() {
            public void run() {
                prepareCamera();
            }
        };
        activity.post(r);
    }

@Override
public void prepareCameraAsync() {
    Runnable r = new Runnable() {
        public void run() {
            prepareCamera();
        }
    };
    activity.post(r);
}

当CameraSurface和camera 对象准备好了的时候(通过检测cameraSurface!=null && cameraSurface.getCamera() != null),就可以让相机由准备状态进入预览模式

    @Override
    public boolean isReady() {
        if (cameraSurface!=null && cameraSurface.getCamera() != null) {
            return true;
        }
        return false;
    }
@Override
public boolean isReady() {
    if (cameraSurface != null && cameraSurface.getCamera() != null) {
        return true;
    }
    return false;
}

异步调用开启预览

    @Override
    public synchronized void startPreviewAsync() {
        Runnable r = new Runnable() {
            public void run() {
                startPreview();
            }
        };
        activity.post(r);
    }
    @Override
    public synchronized void startPreview() {
        // ...and start previewing. From now on, the camera keeps pushing preview
        // images to the surface.
        if (cameraSurface != null && cameraSurface.getCamera() != null) {
            cameraSurface.getCamera().startPreview();
        }
    }

3.由预览模式进行拍照

拍照前还要AndroidDeviceCameraController类设置下相机合适的参数

public void setCameraParametersForPicture(Camera camera) {
        // Before we take the picture - we make sure all camera parameters are as we like them
        // Use max resolution and auto focus
        Camera.Parameters p = camera.getParameters();
        List<Camera.Size> supportedSizes = p.getSupportedPictureSizes();
        int maxSupportedWidth = -1;
        int maxSupportedHeight = -1;
        for (Camera.Size size : supportedSizes) {
            if (size.width > maxSupportedWidth) {
                maxSupportedWidth = size.width;
                maxSupportedHeight = size.height;
            }
        }
        p.setPictureSize(maxSupportedWidth, maxSupportedHeight);
        p.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        camera.setParameters( p );      
    }

接下来,我们将通过设置相机的参数,设置聚焦为自动模式,

@Override
public synchronized void takePicture() {
        // the user request to take a picture - start the process by requesting focus
            setCameraParametersForPicture(cameraSurface.getCamera());
            cameraSurface.getCamera().autoFocus(this);
    }

当聚焦完成后,我们就要拍照了,仅仅实现JPG回调实现

@Override
public synchronized void onAutoFocus(boolean success, Camera camera) {
        // Focus process finished, we now have focus (or not)
        if (success) {
            if (camera != null) {
                camera.stopPreview();
                // We now have focus take the actual picture
                camera.takePicture(null, null, null, this);
            }
        }
    }
@Override
public synchronized void onPictureTaken(byte[] pictureData, Camera camera) {
        this.pictureData = pictureData;
    }

@Override
public synchronized void onAutoFocus(boolean success, Camera camera) {
    // Focus process finished, we now have focus (or not)
    if (success) {
        if (camera != null) {
            camera.stopPreview();                
            /*增加三个回调函数shutterCallback, rawPictureCallback, jpegPictureCallback后,可以进行拍照,并且成功保存*/
            // We now have focus take the actual picture
            camera.takePicture(shutterCallback, rawPictureCallback, jpegPictureCallback);//54wall
            camera.startPreview();
            
        }
    }
}

@Override
public synchronized void onPictureTaken(byte[] pictureData, Camera camera) {
    // We got the picture data - keep it
    this.pictureData = pictureData;
}

ShutterCallback shutterCallback = new ShutterCallback() {
    @Override
    public void onShutter() {
    }
};    

PictureCallback rawPictureCallback = new PictureCallback() {
    @Override
    public void onPictureTaken(byte[] arg0, Camera arg1) {

    }
};

PictureCallback jpegPictureCallback = new PictureCallback() {
    @Override
    public void onPictureTaken(byte[] arg0, Camera arg1) {
        /*可以在Android项目中中生成图片*/
//            String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
//                    .toString()
//                    + File.separator
//                    + "PicTest_" + System.currentTimeMillis() + ".jpg";
//            File file = new File(fileName);
//            if (!file.getParentFile().exists()) {
//                file.getParentFile().mkdir();
//            }
//            
//            try {
//                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
//                bos.write(arg0);
//                bos.flush();
//                bos.close();        
//                
//            } catch (Exception e) {
//                
//            }
        pictureData=arg0;
        
    };
};

以上就是拍照的具体步骤,之后照片完成后,需要将图像数据进行保存到存储器上
拍照的具体功能实现了,为了在libgdx中能够看到摄像头,当然需要在实现ApplicationListener的主类的render()进行设置了,我这里设置了三个按钮,功能分别是开启相机,进行拍摄,和一个控制人物移动的按钮(算是证明是在libgdx框架内部的)。
原代码时触控进入相机预览,松开则进行拍照,我开始还没太理解,render中因为涉及到相机的功能切换,所以在libgdx主类中定义了相机的这几种状态

public enum Mode {
		normal, prepare, preview, takePicture, waitForPictureReady,
	}

render()中的代码非常长,不过就是在相机的各个状态中切换,具体代码如下:

	@Override
	public void render() {
		// Gdx.gl20.glClearColor(0.0f, 0f, 0.0f, 0.0f);//黑
		// Gdx.gl.glClearColor(1, 1, 1, 1);//背景为白色
		Gdx.gl.glClearColor(0.57f, 0.40f, 0.55f, 1.0f);// 紫色
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);// 清屏		
		render_preview();

	}


	public void render_preview() {
		/* 我已经将preview变为takePicture状态移动到click中,实现先预览再拍照,这样便于理解相机的运行步骤 */
		Gdx.gl20.glHint(GL20.GL_GENERATE_MIPMAP_HINT, GL20.GL_NICEST);
		if (mode == Mode.takePicture) {
			Gdx.gl20.glClearColor(0f, 0.0f, 0.0f, 0.0f);
			if (deviceCameraControl != null) {
				deviceCameraControl.takePicture();
			}
			mode = Mode.waitForPictureReady;
		} else if (mode == Mode.waitForPictureReady) {
			Gdx.gl20.glClearColor(0.0f, 0f, 0.0f, 0.0f);
		} else if (mode == Mode.prepare) {
			Gdx.gl20.glClearColor(0.0f, 0.0f, 0f, 0.6f);
			if (deviceCameraControl != null) {
				if (deviceCameraControl.isReady()) {
					deviceCameraControl.startPreviewAsync();
					mode = Mode.preview;
				}
			}
		} else if (mode == Mode.preview) {
			Gdx.gl20.glClearColor(0.0f, 0.0f, 0.0f, 0f);
		} else {
			/* mode = normal */
			Gdx.gl20.glClearColor(0.0f, 0.0f, 0.6f, 1.0f);

		}
		Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
		/* 下边放到texture.bind();时效果一致 */
		batch.begin();	
		stage.act(); // 更新舞台逻辑
		batch.draw(texture, 0, 0, 3f*texture.getWidth(), 3f*texture.getHeight());
		Gdx.app.log("", String.valueOf(texture.getWidth()));
		//先绘制的就先出现,所以演员在texture上边,而不是被覆盖
		//batch.draw(actorTexture, firstActor.getX(), firstActor.getY());//原大小
		batch.draw(actorTexture, firstActor.getX(), firstActor.getY(),4*actorTexture.getWidth(),4*actorTexture.getWidth());//可控制绘制图像大小
		button_move.draw(batch, 1.0f);
		stage.draw();// 绘制舞台
		batch.end();
		Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST);
		Gdx.gl20.glEnable(GL20.GL_TEXTURE);
		Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D);
		// Gdx.gl20.glEnable(GL20.GL_LINE_SMOOTH);//old
		Gdx.gl20.glEnable(GL20.GL_LINE_LOOP);//54wall
		Gdx.gl20.glDepthFunc(GL20.GL_LEQUAL);
		Gdx.gl20.glClearDepthf(1.0F);
		camera.update(true);
		// camera.apply(Gdx.gl20);//old
		texture.bind();

		if (mode == Mode.waitForPictureReady) {
			/*注意deviceCameraControl.getPictureData()得到的是byte[],可见整体思路就是,
			 *将Android摄像头得到byte[],然后将byte[]转换为Pixmap,最后将pixmap存为jpg,这样不适用Android端图片保存模式,
			 *byte[]----Pixmap----jpg
			 */
			if (deviceCameraControl.getPictureData() != null) {
				// camera picture was actually takentake Gdx Screenshot
				Pixmap screenshotPixmap = getScreenshot(0, 0,
						Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
				Pixmap cameraPixmap = new Pixmap(
						deviceCameraControl.getPictureData(), 0,
						deviceCameraControl.getPictureData().length);
				merge2Pixmaps(cameraPixmap, screenshotPixmap);
				// we could call PixmapIO.writePNG(pngfile, cameraPixmap);
				//仅保存screenshot,对同一时间的图片进行保存然后进行比较 
				Pixmap screenshotPixmap_test = getScreenshot(0, 0,
						Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
				FileHandle jpgfile_screenshot = Gdx.files
						.external("a_SDK_fail/libGdxSnapshot" + "_" + date
								+ "_screenshot.jpg");
				deviceCameraControl.saveAsJpeg(jpgfile_screenshot,
						screenshotPixmap_test);
				//仅保存cameraPixma,对同一时间的图片进行保存然后进行比较 
				Pixmap cameraPixmap_test = new Pixmap(
						deviceCameraControl.getPictureData(), 0,
						deviceCameraControl.getPictureData().length);
				FileHandle jpgfile_cameraPixmap = Gdx.files
						.external("a_SDK_fail/libGdxSnapshot" + "_" + date
								+ "_camera.jpg");
				deviceCameraControl.saveAsJpeg(jpgfile_cameraPixmap,
						cameraPixmap_test);
				//保存混合之后的相片
				FileHandle jpgfile = Gdx.files
						.external("a_SDK_fail/libGdxSnapshot" + "_" + date
								+ ".jpg");
				Gdx.app.log("FileHandle", date);
				time_1 = System.currentTimeMillis();
				deviceCameraControl.saveAsJpeg(jpgfile, cameraPixmap);
				time_2 = System.currentTimeMillis();
				//可以得到35830ms=35s,所以非常忙,导致Mode非常缓慢的回到Mode.normal
				Gdx.app.log("cost", String.valueOf(time_2 - time_1));
				deviceCameraControl.stopPreviewAsync();
				//保存文件后,mode回到normal继续render循环,所以中间停顿和logcat长时间未动的其实是卡住了Org
				mode = Mode.normal;

			}
		}
		// 这个log将会一直出现,所以render其实是一直在执行 
		// Gdx.app.log("mode", String.valueOf(i_render++));
	}

如何实现libgdx端的截图

因为AndroidDeviceCameraController 实现两个接口: Camera.PictureCallback,所以可以直接调用,而deviceCameraControl.getPictureData()的byte[]数据则来自AndroidDeviceCameraController,如下

@Override
public synchronized byte[] getPictureData() {
    // Give to picture data to whom ever requested it
    return pictureData;
}
    if (deviceCameraControl.getPictureData() != null) { // camera picture was actually taken
        Pixmap cameraPixmap = new Pixmap(deviceCameraControl.getPictureData(), 0, deviceCameraControl.getPictureData().length);
    }

下面是截图的具体操作过程是保存为pixmap,libgdx中保存格式都是pixmap,而非android中的bitmap

    public Pixmap getScreenshot(int x, int y, int w, int h, boolean flipY) {
        Gdx.gl.glPixelStorei(GL10.GL_PACK_ALIGNMENT, 1);
final Pixmap pixmap = new Pixmap(w, h, Format.RGBA8888);
        ByteBuffer pixels = pixmap.getPixels();
        Gdx.gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixels);
final int numBytes = w * h * 4;
        byte[] lines = new byte[numBytes];
        if (flipY) {
            final int numBytesPerLine = w * 4;
            for (int i = 0; i < h; i++) {
                pixels.position((h - i - 1) * numBytesPerLine);
                pixels.get(lines, i * numBytesPerLine, numBytesPerLine);
            }
            pixels.clear();
            pixels.put(lines);
        } else {
            pixels.clear();
            pixels.get(lines);
        }
return pixmap;
    }

接下来的操作都是需要消耗大量时间和CPU资源的,首先不应放到UI线程中,应该新开线程去执行,并且最好加一个进度条,在代码示例中,我们并没有那么做,所以屏幕在这个过程中会出现卡死的状况。我这里则直接保存了三分文件,分别是截图,android摄像头的拍摄相片,还有二者混合之后的图片,代码如下

/* 仅保存screenshot,对同一时间的图片进行保存然后进行比较 */
                Pixmap screenshotPixmap_test = getScreenshot(0, 0,
                        Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
                FileHandle jpgfile_screenshot = Gdx.files
                        .external("a_SDK_fail/libGdxSnapshot" + "_" + date
                                + "_screenshot.jpg");
                deviceCameraControl.saveAsJpeg(jpgfile_screenshot,
                        screenshotPixmap_test);
                /* 仅保存cameraPixma,对同一时间的图片进行保存然后进行比较 */
                Pixmap cameraPixmap_test = new Pixmap(
                        deviceCameraControl.getPictureData(), 0,
                        deviceCameraControl.getPictureData().length);
                FileHandle jpgfile_cameraPixmap = Gdx.files
                        .external("a_SDK_fail/libGdxSnapshot" + "_" + date
                                + "_camera.jpg");
                deviceCameraControl.saveAsJpeg(jpgfile_cameraPixmap,
                        cameraPixmap_test);
                /* 保存混合之后的相片 */
                FileHandle jpgfile = Gdx.files
                        .external("a_SDK_fail/libGdxSnapshot" + "_" + date
                                + ".jpg");
                Gdx.app.log("FileHandle", date);
                time_1 = System.currentTimeMillis();
                deviceCameraControl.saveAsJpeg(jpgfile, cameraPixmap);
                time_2 = System.currentTimeMillis();
                /* 可以得到35830ms=35s,所以非常忙,导致Mode非常缓慢的回到Mode.normal */
                Gdx.app.log("cost", String.valueOf(time_2 - time_1));
                deviceCameraControl.stopPreviewAsync();

混合两个pixmap

接下来是整合两个PIxmap对象,LibGDX Pixmap对象可以帮助我们实现这个功能,但是因为相机的相片可能有不同的aspect ratio,所以我们也需要分别对待处理

    private void merge2Pixmaps(Pixmap mainPixmap, Pixmap overlayedPixmap) {
        // merge to data and Gdx screen shot - but fix Aspect Ratio issues between the screen and the camera
        Pixmap.setFilter(Filter.BiLinear);
        float mainPixmapAR = (float)mainPixmap.getWidth() / mainPixmap.getHeight();
        float overlayedPixmapAR = (float)overlayedPixmap.getWidth() / overlayedPixmap.getHeight();
        if (overlayedPixmapAR < mainPixmapAR) {
            int overlayNewWidth = (int)(((float)mainPixmap.getHeight() / overlayedPixmap.getHeight()) * overlayedPixmap.getWidth());
            int overlayStartX = (mainPixmap.getWidth() - overlayNewWidth)/2;
            mainPixmap.drawPixmap(overlayedPixmap, 
                        0, 
                        0, 
                        overlayedPixmap.getWidth(), 
                        overlayedPixmap.getHeight(), 
                        overlayStartX, 
                        0, 
                        overlayNewWidth, 
                        mainPixmap.getHeight());
        } else {
            int overlayNewHeight = (int)(((float)mainPixmap.getWidth() / overlayedPixmap.getWidth()) * overlayedPixmap.getHeight());
            int overlayStartY = (mainPixmap.getHeight() - overlayNewHeight)/2;
            mainPixmap.drawPixmap(overlayedPixmap, 
                        0, 
                        0, 
                        overlayedPixmap.getWidth(), 
                        overlayedPixmap.getHeight(), 
                        0, 
                        overlayStartY, 
                        mainPixmap.getWidth(), 
                        overlayNewHeight);                  
        }
    }

将图片保存为jpg

所以我们选择JPG格式进行保存,一种方式就是使用Android的bitmap类对图片进行jpg格式的保存,这个功能可以在AndroidDeviceController类中实现,因为它是Android特有的功能,所以我们想不用它。
尽量将大部分代码全部放到libgdx框架中,就是大部分实现的代码要在core中,然而libgdx的pixel格式是RGBA,而bitmap的Pixmap格式是ARGB,所以我们需要一bit一bit的将颜色转换过来

 @Override
    public void saveAsJpeg(FileHandle jpgfile, Pixmap pixmap) {
        FileOutputStream fos;
        int x=0,y=0;
        int xl=0,yl=0;
        try {
            Bitmap bmp = Bitmap.createBitmap(pixmap.getWidth(), pixmap.getHeight(), Bitmap.Config.ARGB_8888);
            // we need to switch between LibGDX RGBA format to Android ARGB format
            for (x=0,xl=pixmap.getWidth(); x<xl;x++) {
                for (y=0,yl=pixmap.getHeight(); y<yl;y++) {
                    int color = pixmap.getPixel(x, y);
                    // RGBA => ARGB
                    int RGB = color >> 8;
                    int A = (color & 0x000000ff) << 24;
                    int ARGB = A | RGB;
                    bmp.setPixel(x, y, ARGB);
                }
            }
            // Finished Color format conversion
            fos = new FileOutputStream(jpgfile.file());
            bmp.compress(CompressFormat.JPEG, 90, fos);
            // Finished Comression to JPEG file
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();            
        }
    }

停止预览

在完成保存图片后,我们将停止预览窗口,并且从Activity窗口中移去CameraSurface,我们同样也将停止camera继续想camera surface继续发送preview,我们同样异步执行这些。

 @Override
    public synchronized void stopPreviewAsync() {
        Runnable r = new Runnable() {
            public void run() {
                stopPreview();
            }
        };
        activity.post(r);
    }
@Override
    public synchronized void stopPreview() {
        // stop previewing. 
        if (cameraSurface != null) {
            if (cameraSurface.getCamera() != null) {
                cameraSurface.getCamera().stopPreview();
            }
            ViewParent parentView = cameraSurface.getParent();
            if (parentView instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) parentView;
                viewGroup.removeView(cameraSurface);
            }
        }
    }

注意在合成两张pixmap也就是混合相机取景和libgdx截图时分辨率有差异。在我们的Pixmaps整合过程中依然还存在一个问题,就是相机的分辨率和我们的截图分辨率也许是不同的(我在我的三星手机上,我把一个480x320屏幕截图延伸到2560x1920 大小的图片)。一个围绕它的解决方法就是扩大Libgdx视图的尺寸到更大,比实际物理设备的尺寸要大,要实现他需要使用setFixedSize()功能。真实的屏幕尺寸是根据BPU内存。
然而,在这个时间中我设法去设置虚拟屏幕尺寸为960x640(可能因为GPU显存已经被origin的带下分配了)

 public void setFixedSize(int width, int height) {
        if (graphics.getView() instanceof SurfaceView) {
            SurfaceView glView = (SurfaceView) graphics.getView();
            glView.getHolder().setFixedSize(width, height);
            glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        }
    }
public void restoreFixedSize() {
        if (graphics.getView() instanceof SurfaceView) {
            SurfaceView glView = (SurfaceView) graphics.getView();
            glView.getHolder().setFixedSize(origWidth, origHeight);
            glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        }
    }

一些小提示

提示1
注意,代码仅仅适用于Android平台,并非跨平台解决方法,但是我觉得至少在桌面端,应该可以提供类似功能的代码
提示2
在整合图片,并将图片数据保存到存储器上时,会花大量的时间,是因为libgdx颜色方案是RGBA而bitmap颜色方案是ARGB
提示3
最后需要注意的
我仅仅测试了一部分Android设备,不同的GPU会产生不同的现象。
完整代码已上传github
https://github.com/54wall/LibgdxAndroidCamera
最后的截图,按下camera可以开启相机,看下shot,就是进行拍摄,move可以控制猫向右侧移动
4.jpg

2018-02-08 14:40:52 xy1213236113 阅读数 7596
  • Android底层技术:HAL驱动开发

    本课程提供开发者学习Android底层的HAL(硬件抽象层)的开发方法和技术。HAL所在的位置是介于Android系统服务与Linux内核之间,HAL Driver是以library形式出现,给HAL Stub调用,供Android System架构者调用。而HAL Stub则是google设计出来的,保护硬件厂商的硬件驱动。

    17825 人正在学习 去看看 高煥堂

使用opencv去访问android设备摄像头, C++库是无法获取到android设备硬件的,所有需要借助Opencv对android提供的java库进行访问android设备摄像头。在opencv官方下载AndroidSDK,导入项目中就可以使用了。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <!--JavaCameraView最终继承自SurfaceView,显示摄像头帧数据的展示-->
    <org.opencv.android.JavaCameraView
        android:id="@+id/cameraView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:camera_id="back"//后置摄像头
        />
</android.support.constraint.ConstraintLayout>

JavaCameraView继承自CameraBridgeViewBase,CameraBridgeViewBase又继承自SurfaceView,JavaCameraView是可以显示摄像头捕获到的帧数据的View,CameraBridgeViewBase类中CvCameraViewListener2接口提供了摄像头onCameraViewStarted、onCameraViewStopped以及onCameraFrame回调。我们要对摄像头捕获的每一帧数据进行操作就需要再OnCameraFrame回调中进行处理。通过一个native函数,在将摄像头的每一帧数据地址传给C++,对帧数据进行操作。代码如下:

public class CameraOpenCVActivity extends AppCompatActivity implements CameraBridgeViewBase.CvCameraViewListener2 {//
    private JavaCameraView cameraView;
    private Mat rgba;
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_opencv);
        cameraView = findViewById(R.id.cameraView);
        cameraView.setCvCameraViewListener(this);
        //权限请求
        RxPermissions rxPermission = new RxPermissions(this);
        rxPermission
                .requestEachCombined(Manifest.permission.CAMERA,
                        Manifest.permission.READ_EXTERNAL_STORAGE)
                .subscribe(permission -> {
                    if (permission.granted) {
                    } else if (permission.shouldShowRequestPermissionRationale) {
                    } else {
                    }
                });
    }
    //NDK对每一帧数据进行操作
    public static native void nativeRgba(long jrgba);

    /**
     *  当摄像机预览开始时,这个方法就会被调用。在调用该方法之后,框架将通过onCameraFrame()回调向客户端发送。
     *
     * @param width  - 帧的宽度
     * @param height - 帧的高度
     */
    @Override
    public void onCameraViewStarted(int width, int height) {
        //定义Mat对象
        rgba = new Mat(width, height, CvType.CV_8UC4);
    }

    /**
     * 当摄像机预览由于某种原因被停止时,这个方法就会被调用。
     *在调用这个方法之后,不会通过onCameraFrame()回调来传递任何帧。
     */
    @Override
    public void onCameraViewStopped() {
        rgba.release();
    }

    /**
     * 当需要完成框架的交付时,将调用此方法。
     *返回值-是一个修改后的帧,需要在屏幕上显示。
     * TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
     *
     * @param inputFrame
     */
    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        rgba = inputFrame.rgba();
        //得到当前一帧图像的内存地址
        long addr = rgba.getNativeObjAddr();
        //对一帧图像进行处理
        nativeRgba(addr);
        //得到一帧灰度图
//        rgba = inputFrame.gray();
        return rgba;
    }


    @Override
    protected void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
        } else {
            cameraView.enableView();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (cameraView != null) {
            cameraView.disableView();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (cameraView != null) {
            cameraView.disableView();
        }
    }
}

在C++中对每一帧数据进行单通道处理,并做了SobelX和Y方向的边缘检测,代码和结果如下:

 Mat &img = *(Mat*)jrgba;
    Mat out;
    cvtColor(img,out,COLOR_RGBA2GRAY);
    Mat outimgX,outimgY,outXY;
    //x方向sobel梯度
    Sobel(out,outimgX,CV_8U,1,0);
    //y方向sobel梯度
    Sobel(out,outimgY,CV_8U,0,1);
    //合并梯度
    addWeighted(outimgX,0.5,outimgY,0.5,0,outXY);
    uchar *ptr = img.ptr(0);
    uchar *outPtr = outXY.ptr(0);
    for (int i = 0; i < img.rows * img.cols; ++i) {
        ptr[4*i+0] = outPtr[I];
        ptr[4*i+1] = outPtr[I];
        ptr[4*i+2] = outPtr[I];
    }

Screenshot_2018-02-07-23-37-12-648.png

当前我使用的是后置摄像头,我们又发现了一个问题,帧数据是显示出来,但是图像是横着的,怎么正过来呢?很简单,我们既然获取到了每一帧数据的矩阵,那么我们直接就可以对矩阵进行操作。原理与代码如下:

 Mat &img = *(Mat*)jrgba;

    Mat imgT(height,width,CV_8UC4);
    Mat imgF(height,width,CV_8UC4);
//    //图片倒置
    transpose(img,imgT);
    /**
   * 重定义图片大小
   * 第三个参数dsize:size格式的图片大小
   * 第四个参数dx:x方向的图片缩放比
   * 第五个参数dy:y方向的缩放比
   * 第六个参数:插值方式,一般情况使用默认值(线性插值)
   * dsize不为0的时候,dx和dy是无效的
   *
   */
    resize(imgT,imgF,Size(width,height));
    /**
     * 翻转图片
     * 第三个参数,0以x轴进行翻转,1以y轴翻转,-1以xy同时翻转
     */
    flip(imgF,img,1);

Screenshot_2018-02-07-23-30-53-933.png

哈哈!图像正过来了吧!当前是后置摄像头,那么前置摄像头会不会也是这样的解决办法呢?那我们代码撸起,看那看会如何呢?

Screenshot_2018-02-07-23-00-17-594.png

此时我们发现,用于后置摄像头相同的操作去解决,并没有达到预期的效果,很明显图像是倒置的并且左右方向也是反的。于是乎,深思之后,发现前置摄像头与后置摄像头的图像其实就相当于一个照镜子的过程,我们可以在倒置矩阵之后,再进行一次X,Y方向上的同时矩阵翻转,不就可以了,一切想明白了之后,试了试,果然达到了预期目标效果。图像与代码如下:

 Mat &img = *(Mat*)jrgba;
    Mat imgT(height,width,CV_8UC4);
    Mat imgF(height,width,CV_8UC4);
//    //图片倒置
    transpose(img,imgT);
    /**
   * 重定义图片大小
   * 第三个参数dsize:size格式的图片大小
   * 第四个参数dx:x方向的图片缩放比
   * 第五个参数dy:y方向的缩放比
   * 第六个参数:插值方式,一般情况使用默认值(线性插值)
   * dsize不为0的时候,dx和dy是无效的
   *
   */
    resize(imgT,imgF,Size(width,height));
    /**
     * 翻转图片
     * 第三个参数,0以x轴进行翻转,1以y轴翻转,-1以xy同时翻转
     */
    flip(imgF,img,-1);//进行XY方向同时翻转

Screenshot_2018-02-07-23-12-00-300.png

哈哈!我又离人脸识别进了一步,很开森的结束了这篇博客!

本文章著作版权所属:微笑面对,请关注我的CSDN博客:博客地址

2016-06-11 14:10:06 liuyi1207164339 阅读数 7588
  • Android底层技术:HAL驱动开发

    本课程提供开发者学习Android底层的HAL(硬件抽象层)的开发方法和技术。HAL所在的位置是介于Android系统服务与Linux内核之间,HAL Driver是以library形式出现,给HAL Stub调用,供Android System架构者调用。而HAL Stub则是google设计出来的,保护硬件厂商的硬件驱动。

    17825 人正在学习 去看看 高煥堂

很多时候我们需要调用系统摄像头进行拍照或者从图库选择照片,然后对得到的图片进行一些处理。比如微信设置头像,就可以选择调用设备存在摄像头APP进行拍照,然后对图像进行裁剪,最终设置为头像。


下面来进行讲解。


主要分为以下几个部分:

1、获得摄像头Feature和写文件的权限

由于不需要直接操作摄像头,而只是调用存在的APP,所以不需要Camera权限,自需要获得Camera Feature即可:

<uses-feature
        android:name="android.hardware.camera2"
        android:required="true" />

如果需要把得到的图片写入外部存储,需要声明写外部存储权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

如果需要把得到的图片写入私有存储,在Android4.3及以下,需要声明WRITE_EXTERNAL_STORAGE权限,从Android4.4开始,这个权限不再需要了,所以可以声明权限如下所示:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
 android:maxSdkVersion="18" />

2、创建一个文件用来保存得到的图片

/**
     * 创建保存得到的图片的文件
     *
     * @return
     * @throws IOException
     */
    private File createImageFile() throws IOException {
        if (checkSDCardAvaliable()) {
            File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_PICTURES), "CameraDemo");
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    Log.d(TAG, "failed to create directory");
                    return null;
                }
            }
            // Create an image file name
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String imageFileName = "JPEG_" + timeStamp;
            String suffix = ".jpg";
            File image = new File(mediaStorageDir + File.separator + imageFileName + suffix);
            mCurrentPhotoPath = image.getAbsolutePath();
            return image;
        }
        return null;
    }


3、启动Intent进行拍照、启动Intent从图库选择照片

  /**
     * 启动拍照
     */
    private void startCamera() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // Ensure that there's a camera activity to handle the intent
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            // Continue only if the File was successfully created
            if (mSavePhotoFile != null) {
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                        Uri.fromFile(mSavePhotoFile));//设置文件保存的URI
                startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
            }
        }
    }

 /**
     * 从图库选择照片
     */
    private void selectPhoto() {
        Intent intent = new Intent(Intent.ACTION_PICK,
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, REQUEST_SELECT_PHOTO);
    }


4、在onActivityResult回调函数里面,对图片进行处理,包括对图片进行裁剪以及显示到ImageView以及把图片加入到Media Provider's database

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case REQUEST_TAKE_PHOTO:  //拍照
                    //注意,如果拍照的时候设置了MediaStore.EXTRA_OUTPUT,data.getData=null
                    startPhotoZoom(Uri.fromFile(mSavePhotoFile), 256, 256);
                    break;
                case REQUEST_SELECT_PHOTO://选择图片
                    startPhotoZoom(data.getData(), 256, 256);
                    break;
                case REQUEST_CROP_PHOTO:
                    Bundle extras = data.getExtras();
                    if (extras != null) {
                        Bitmap photo = extras.getParcelable("data");
                        //把图片显示到ImgeView
                        iv_diaplay.setImageBitmap(photo);
                        //把图片加入图库
                        galleryAddPic();
                    }
                    break;
            }
        }
    }


裁剪照片:

 /**
     * 裁剪照片
     */
    public void startPhotoZoom(Uri uri, int width, int height) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        // 设置裁剪
        intent.putExtra("crop", "true");
        intent.putExtra("scale", true);// 去黑边
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", width/height);
        intent.putExtra("aspectY", 1);
        // outputX outputY 是裁剪图片宽高
        intent.putExtra("outputX", width);
        intent.putExtra("outputY", height);
        // 图片格式
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);// 取消人脸识别
        intent.putExtra("return-data", true);// true:返回uri,false:不返回uri
        // 同一个地址下 裁剪的图片覆盖之前得到的图片
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mSavePhotoFile));
        startActivityForResult(intent, REQUEST_CROP_PHOTO);
    }

把图片加入Media Provider's database

 /**
     * 触发系统的media scanner来把图片加入Media Provider's database
     */
    private void galleryAddPic() {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        File f = new File(mCurrentPhotoPath);//
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);  //设置URI
        this.sendBroadcast(mediaScanIntent);  //发送广播
    }
把Bitmap保存到外部存储
/**
     * 把图片保存到SD卡
     * @param bitmap
     * @param targetPath
     */
    public static void SavePhotoToSdCard(Bitmap bitmap, String targetPath) {

        FileOutputStream fileOutputStream = null;
        File file = new File(targetPath);
        try {
            fileOutputStream = new FileOutputStream(file);
            if (bitmap != null) {
                if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100,
                        fileOutputStream)) {
                    fileOutputStream.flush();
                }
            }
        } catch (FileNotFoundException e) {
            file.delete();
            e.printStackTrace();
        } catch (IOException e) {
            file.delete();
            e.printStackTrace();
        } finally {
            try {
                // 到最后一定要关闭
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
然后,你就可以在你设备的外部存储根目录下的/Pictures/CameraDemo/目录下面找到你的图片,同时在设备的图库里面发现一个新建项"CameraDemo",里面有拍照或者从图库选择得到的图片。



2018-03-19 17:34:06 CLinuxF 阅读数 1566
  • Android底层技术:HAL驱动开发

    本课程提供开发者学习Android底层的HAL(硬件抽象层)的开发方法和技术。HAL所在的位置是介于Android系统服务与Linux内核之间,HAL Driver是以library形式出现,给HAL Stub调用,供Android System架构者调用。而HAL Stub则是google设计出来的,保护硬件厂商的硬件驱动。

    17825 人正在学习 去看看 高煥堂

        前面写完了Qt for Android 调用系统摄像头和相册后,发现并不满足一些人的特殊需求,比如需要调用摄像头来录像,虽然不知道这个功能能用到什么地方,但是有人提出了,我还是实现一下。如果没有看过我前面Qt for Android 调用系统摄像头和相册这篇博客的请先转移看一下,毕竟是在这篇博客的基础上加的一个新功能,所以,我就只列出如何实现录像部分的核心代码了:

在前面的代码工程中插入下面的代码块:(因为代码不好贴,所以只显示图片,为了排版美观)


然后在StartActivityForResult回调中处理录像好的数据路径,如下图:


这个videoPath就是所播放的路径了,只要把这个路径再传送给QT那边接收,即可播放视频,下面展示一下播放的处理:


        因为是在qml播放的,获取路径播放时发现视频是倒置的,自动倒置的,具体原因也不清楚,后来自己的处理了一下,将orientation这个属性直接旋转180度,当然如果视频是正常的,那这样设置就会导致正常视频倒置了。希望知道此原因的朋友,可以告知一下。

        完整Demo:https://download.csdn.net/download/clinuxf/10295906

Android 摄像头开发

阅读数 1514

没有更多推荐了,返回首页