Android之 Camera相机使用

这篇具有很好参考价值的文章主要介绍了Android之 Camera相机使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一 简介

1.1 随着信息时代的发展,相机在我们生活中使用越来越频繁,也成为手机的基本配置之一。相机可以用来拍照,拍视频,人脸识别,视频聊天,扫码支付,监控等常见领域

不管什么场景,基本原理都差不多,都要先通过相机采集原始数据,也就是二进制字节数据,我们可以对原始数据做对应的操作,比如保存成图片,或者分析数据内容等等。

1.2 Android相机的API到目前发展了3个版本,如下面官方api文档所示

android camera,android,数码相机

Camera
此类是用于控制设备相机的旧版 API,现已弃用,在Android5.0以下使用
Camera2
此软件包是用于控制设备相机的主要 API,Android5.0以上使用
CameraX
基于Camera 2 API封装,简化了开发流程,并增加生命周期控制

1.3 各版本优点

Camera

  • 检测设备摄像头,打开相机
  • 创建预览画面,显示实时预览画面
  • 设置相机参数,进行拍照监听
  • 监听中,保存图片资源或者直接操作原始数据
  • 释放相机资源

Camera2

  • 改进了新硬件的性能。Supported Hardware Level的概念,不同厂商对Camera2的支持程度不同,从低到高有LEGACY、LIMITED、FULL 和 LEVEL_3四个级别
  • 以更快的间隔拍摄图像
  • 显示来自多个摄像机的预览
  • 直接应用效果和滤镜

CameraX

  • CameraX 是一个 Jetpack 支持库,目的是简化Camera的开发工作,它是基于Camera2 API的基础,向后兼容至 Android 5.0(API 级别 21)。
  • 易用性,只需要几行代码就可以实现预览和拍照
  • 保持设备的一致性,在不同相机设备上,对宽高比、屏幕方向、旋转、预览大小和高分辨率图片大小,做到都可以正常使用
  • 相机特性的扩展,增加人像、HDR、夜间模式和美颜等功能

1.4 如果对相机要求不高,只是单纯的解析数据,那用Camera就可以了。虽然官方说已过时,但还是有非常多的场合使用。 

二 基于扫码支付的Camera的使用 

2.1 添加相机权限

<uses-feature
   android:name="android.hardware.camera"
   android:required="true" />
 
<uses-permission android:name="android.permission.CAMERA" />

2.2 检查相机权限

//权限请求
public final int REQUEST_CAMERA_PERMISSION = 1;
private String cameraPermission = Manifest.permission.CAMERA;

private void checkCameraPermission() {
	//检查是否有相机权限
	if (ContextCompat.checkSelfPermission(this, cameraPermission) != PackageManager.PERMISSION_GRANTED) {
		//没权限,请求权限
		ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
				REQUEST_CAMERA_PERMISSION);
	} else {
		//有权限
		createSurfaceView();
	}
}

//权限请求回调
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
	switch (requestCode) {
		case REQUEST_CAMERA_PERMISSION:
			if (grantResults != null && grantResults.length > 0
					&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
				//用户同意权限
				createSurfaceView();
			} else {
				// 权限被用户拒绝了,可以提示用户,关闭界面等等。
				Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
			}
			break;
	}
}

2.3 用SurfaceView来加载相机画面

/**
 * 创建预览画面
 */
private SurfaceView surfaceView;
private SurfaceHolder mHolder;

public void createSurfaceView() {
	//创建预览
	surfaceView = new SurfaceView(this);
	flContent.removeAllViews();
	flContent.addView(surfaceView);
	//获取预览的管理器
	mHolder = surfaceView.getHolder();
	//监听预览状态
	mHolder.addCallback(new SurfaceHolder.Callback() {
		@Override
		public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
			//预览控件创建成功的时候,打开相机并预览
			openCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
		}

		@Override
		public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {

		}

		@Override
		public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
			//预览控件销毁的时候,释放相机资源
			releaseCamera();
		}
	});
}

2.4 打开相机

private Camera mCamera;
private Camera.CameraInfo cameraInfo;

/**
 * 打开相机
 * Camera.CameraInfo.CAMERA_FACING_FRONT前置
 * Camera.CameraInfo.CAMERA_FACING_BACK后置
 *
 * @param cameraIndex 摄像头的方位
 */
public void openCamera(int cameraIndex) {
	try {
		//先释放相机资源
		releaseCamera();
		//获取相机信息
		if (cameraInfo == null) {
			cameraInfo = new Camera.CameraInfo();
		}
		//获取相机个数
		int cameraCount = Camera.getNumberOfCameras();
		//由于不知道第几个是前置摄像头,遍历获取前置摄像头
		for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
			Camera.getCameraInfo(camIdx, cameraInfo);

			if (cameraInfo.facing == cameraIndex) {
				mCamera = Camera.open(camIdx);
				break;
			}
		}

		//开启预览
		startPreview();
	} catch (Exception e) {
		//获取相机异常
		mCamera = null;
	}
}

2.5 开始预览

/**
 * 开始预览
 */
public void startPreview() {
	try {
		//获取屏幕宽高,预览尺寸默认为屏幕的屏幕
		WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
		Display display = manager.getDefaultDisplay();
		int shortSize = display.getWidth();
		int longSize = display.getHeight();
		//设置相机参数
		initPreviewParams(shortSize, longSize);
		//设置相机方向
		adjustCameraOrientation();
		//预览方式一,没缓冲区,会频繁GC
		//mCamera.setPreviewCallback(previewCallback);
		//绑定预览视图
		mCamera.setPreviewDisplay(mHolder);
		//设置缓冲区
		mCamera.addCallbackBuffer(new byte[shortSize * longSize * 3 / 2]);
		mCamera.setPreviewCallbackWithBuffer(previewCallback);
		//开始预览
		mCamera.startPreview();
	} catch (IOException e) {

	}
}

2.6 配置相机参数和预览大小

/**
 * 设置相机参数
 */
private void initPreviewParams(int shortSize, int longSize) {
	if (mCamera != null) {
		Camera.Parameters parameters = mCamera.getParameters();
		//获取手机支持的尺寸
		List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
		//获取合适的预览尺寸,保证不变形
		Camera.Size bestSize = getBestSize(shortSize, longSize, sizes);
		//设置预览大小
		parameters.setPreviewSize(bestSize.width, bestSize.height);
		//设置图片大小,拍照
		parameters.setPictureSize(bestSize.width, bestSize.height);
		//设置格式,所有的相机都支持 NV21格式
		parameters.setPreviewFormat(ImageFormat.NV21);
		//设置聚焦,如果拍照就设置持续对焦FOCUS_MODE_CONTINUOUS_PICTURE,其它可以自动对焦FOCUS_MODE_AUTO
		parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

		mCamera.setParameters(parameters);
	}
}

/**
 * 获取预览最佳尺寸
 */
private Camera.Size getBestSize(int shortSize, int longSize, List<Camera.Size> sizes) {
	Camera.Size bestSize = null;
	float uiRatio = (float) longSize / shortSize;
	float minRatio = uiRatio;
	for (Camera.Size previewSize : sizes) {
		float cameraRatio = (float) previewSize.width / previewSize.height;

		//如果找不到比例相同的,找一个最近的,防止预览变形
		float offset = Math.abs(cameraRatio - minRatio);
		if (offset < minRatio) {
			minRatio = offset;
			bestSize = previewSize;
		}
		//比例相同
		if (uiRatio == cameraRatio) {
			bestSize = previewSize;
			break;
		}

	}
	return bestSize;
}

2.7 配置预览方向

/**
 * 调整预览方向
 * 由于手机的图片数据都来自摄像头硬件传感器,这个传感器默认的方向横向的,所以要根据前后摄像头调整方向
 */
private void adjustCameraOrientation() {
	//判断当前的横竖屏
	int rotation = getWindowManager().getDefaultDisplay().getRotation();

	int degress = 0;
	//获取手机的方向
	switch (rotation) {
		case Surface.ROTATION_0:
			degress = 0;
			break;
		case Surface.ROTATION_90:
			degress = 90;
			break;
		case Surface.ROTATION_180:
			degress = 180;
			break;
		case Surface.ROTATION_270:
			degress = 270;
			break;
	}
	int result = 0;
	if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
		//后置摄像头
		result = (cameraInfo.orientation - degress + 360) % 360;
	} else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
		//前置摄像头,多一步镜像
		result = (cameraInfo.orientation + degress) % 360;
		result = (360 - result) % 360;
	}
	mCamera.setDisplayOrientation(result);
}

 2.8 监听预览数据

 /**
 * 预览数据监听
 */
Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
	public void onPreviewFrame(byte[] data, Camera camera) {
		if (data != null) {
			//获取预览分辨率
			Camera.Parameters parameters = camera.getParameters();
			Camera.Size size = parameters.getPreviewSize();
			//拿到字节数组,可以生成图片,也可以解析数据(比如二维码扫描,人脸识别)
			//................................

//                //创建解码图像,并转换为原始灰度数据,注意图片是被旋转了90度的
//                Image source = new Image(size.width, size.height, "Y800");
//                //图片旋转了90度,将扫描框的TOP作为left裁剪
//                source.setData(data);//填充数据
//                //解码,返回值为0代表失败,>0表示成功
//                int dataResult = mImageScanner.scanImage(source);
		}

		//不管有没有数据,重新设置缓冲区,避免频繁GC
		camera.addCallbackBuffer(data);
	}
};

2.9  拍照并保存照片

/**
 * 拍照
 */
public void takePicture() {
	if (mCamera == null) {
		Toast.makeText(this, "请打开相机", Toast.LENGTH_SHORT).show();
		return;
	}
	mCamera.takePicture(new Camera.ShutterCallback() {
		@Override
		public void onShutter() {

		}
	}, null, new Camera.PictureCallback() {
		@Override
		public void onPictureTaken(byte[] data, Camera camera) {
			new SavePicAsyncTask(CameraActivity.this, cameraInfo.facing, data).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
		}
	});
}

/**
 * 保存图片
 */
class SavePicAsyncTask extends AsyncTask<Void, Void, File> {
	Context context;
	int facing;
	byte[] data;

	public SavePicAsyncTask(Context context, int facing, byte[] data) {
		this.context = context;
		this.facing = facing;
		this.data = data;
	}


	@Override
	protected File doInBackground(Void... voids) {
		//保存的文件
		File picFile = null;
		try {
			Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
			if (bitmap == null) {
				return null;
			}
			//保存之前先调整方向
			if (facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
				Matrix matrix = new Matrix();
				matrix.postRotate(90);
				bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
			} else {
				Matrix matrix = new Matrix();
				matrix.postRotate(270);
				bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
			}

			// SD卡根目录
			File dir = context.getExternalFilesDir("print");
			if (!dir.exists()) {
				dir.mkdirs();
			}
			picFile = new File(dir, System.currentTimeMillis() + ".jpg");
			FileOutputStream fos = new FileOutputStream(picFile);
			bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
			fos.close();
			bitmap.recycle();
			return picFile;

		} catch (Exception e) {
			e.printStackTrace();
		}
		return picFile;
	}

	@Override
	protected void onPostExecute(File file) {
		super.onPostExecute(file);
		if (file != null) {
			Toast.makeText(context, "图片保存成功", Toast.LENGTH_SHORT).show();
		} else {
			Toast.makeText(context, "图片保存失败", Toast.LENGTH_SHORT).show();
		}
	}
}

2.10 释放相机资源

/**
 * 释放相机资源
 */
private void releaseCamera() {
	if (mCamera != null) {
		mCamera.stopPreview();
		mCamera.stopFaceDetection();
		mCamera.setPreviewCallback(null);
		mCamera.release();
		mCamera = null;
	}
}

三 重点注意

3.1 对焦模式setFocusMode

FOCUS_MODE_AUTO:自动对焦
FOCUS_MODE_INFINITY:无穷远
FOCUS_MODE_MACRO:微距
FOCUS_MODE_FIXED:固定焦距
FOCUS_MODE_EDOF:景深扩展
FOCUS_MODE_CONTINUOUS_PICTURE:持续对焦(针对照片)
FOCUS_MODE_CONTINUOUS_VIDEO:(针对视频)

3.2  预览格式setPreviewFormat,默认返回NV21的数据

ImageFormat.NV16
ImageFormat.NV21
ImageFormat.YUY2
ImageFormat.YV12
ImgaeFormat.RGB_565
ImageFormat.JPEG

3.3  设置预览尺寸,setPreviewSize。根据屏幕尺寸设备最佳预览尺寸

/**
 * 获取预览最佳尺寸
 */
private Camera.Size getBestSize(int shortSize, int longSize, List<Camera.Size> sizes) {
    Camera.Size bestSize = null;
    float uiRatio = (float) longSize / shortSize;
    float minRatio = uiRatio;
    for (Camera.Size previewSize : sizes) {
        float cameraRatio = (float) previewSize.width / previewSize.height;

        //如果找不到比例相同的,找一个最近的,防止预览变形
        float offset = Math.abs(cameraRatio - minRatio);
        if (offset < minRatio) {
            minRatio = offset;
            bestSize = previewSize;
        }
        //比例相同
        if (uiRatio == cameraRatio) {
            bestSize = previewSize;
            break;
        }

    }
    return bestSize;
}

3.4  预览方向,上面说了,传感器方向默认横向的,所以预览成像要调整方向。

下图是传感器方向和需要调整的角度图示

android camera,android,数码相机

 3.5 拍照图片方向,由于拍照也是存储的传感器方向,所以也需要做旋转

保存之前先调整方向
if (facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
     bitmap = BitmapUtils.rotate(bitmap, 90);
} else {
     bitmap = BitmapUtils.rotate(bitmap, 270);
文章来源地址https://www.toymoban.com/news/detail-608629.html

到了这里,关于Android之 Camera相机使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 十分钟实现 Android Camera2 相机预览

    因为工作中要使用 Android Camera2 API ,但因为 Camera2 比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在 CSDN 上记录了下,希望能帮助到更多的小伙伴。 Camera2 API 的包名是 android.hardware.camera2 ,是 Android 5.0 后推出的一套调用摄像头设备的接口

    2024年02月13日
    浏览(70)
  • 十分钟实现 Android Camera2 相机拍照

    因为工作中要使用 Android Camera2 API ,但因为 Camera2 比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在 CSDN 上记录了下,希望能帮助到更多的小伙伴。 上篇文章 我们使用 Camera2 实现了相机预览的功能,这篇文章我们接着上文,来实现 Cam

    2024年02月11日
    浏览(63)
  • Android中相机(Camera)画面旋转角度分析:手机摄像头的“正向”、手机画面自然方向、相机画面的偏转角度

    #.概述:     1.如同人眼看东西分上下一样,摄像头也有其“正向”,正常情况下,Android手机后置、前置摄像头的“正向”朝向为手机的“右侧”(默认如此,除非手机厂商修改设置)。 (这里运行代码做过测试发现,前置摄像头也是以右侧为正向,而不是有些资料上说的

    2024年02月14日
    浏览(77)
  • Android Camera开发入门(4):USB/UVC Camera的使用

    本文基于开源项目https://github.com/saki4510t/UVCCamera之上进行二次封装和使用 在前几篇文章中,我们介绍了Camera到CameraX的基础功能应用,同时附上了相关代码,需要的源码的大佬们可以滑到最底部获取。 本篇我们一起来了解 USB/UVC 相机是什么以及它们与传统相机的区别,并按照

    2024年02月06日
    浏览(44)
  • Android 之 使用 Camera 拍照

    本节给大家带来的是Android中Camera的使用,简单点说就是拍照咯,无非两种: 1.调用系统自带相机拍照,然后获取拍照后的图片 2.要么自己写个拍照页面 本节我们来写两个简单的例子体验下上面的这两种情况~ 我们只需下面一席话语,即可调用系统相机,相机拍照后会返回一个

    2024年02月09日
    浏览(40)
  • android camera系列(Camera1、Camera2、CameraX)的使用以及输出的图像格式

    1.1.1、布局 1.1.2、实现预览 Camera.open() 打开摄像头 setPreviewDisplay 设置预览展示的控件 startPreview 开始预览 发现预览是横着的,需要使用 setDisplayOrientation 调整预览图像的方向 1.1.3、获取摄像头的原始数据 setPreviewCallback 设置预览数据的回调 2560*1440 默认返回图像的分辨率 Image

    2024年02月21日
    浏览(51)
  • micropython 自制数码相机

    像头(CAMERA或WEBCAM)又称为电脑相机、电脑眼、电子眼等,是一种视频输入设备,被广泛的运用于视频 会议,安防系统  、图像采集系统、 环境监控 、工业现场过程控制 等方面。本实验用TPYBoard  v102以 及PTC06 串口摄像头模块DIY一个简易的照相机。 1.所用器材:    TPY

    2024年02月19日
    浏览(50)
  • Android Camera开发入门(3):CameraX的使用

    CameraX API简介 在前两篇博客中,我们介绍了Camera基础知识和Camera2 API的使用。为了进一步简化相机应用开发,Google推出了CameraX API,它提供了一个更加简洁、易于使用的接口,帮助开发者快速实现高质量的相机功能。本篇博客将带领你了解CameraX的使用方法,并提供相应的示例

    2024年02月11日
    浏览(37)
  • Android之camera1和2的简单使用

    前言 Android Framework提供Camera API来实现拍照与录制视频的功能,目前Android有三类API, Camera 此类是用于控制设备相机的旧版 API,现已弃用,在Android5.0以下使用 Camera2 此软件包是用于控制设备相机的主要 API,Android5.0以上使用 CameraX 基于Camera 2 API封装,简化了开发流程,并增加

    2024年02月09日
    浏览(44)
  • Android多媒体功能开发(12)——使用Camera类拍照

    Android上用摄像头拍照、录视频有两套API可用,Android5.0(API21)之前使用android.hardware.Camera类,之后推荐使用android.hardware.camera2包。目前这两套API都可以使用,Camera类用起来比较简单易懂,但功能少灵活性差,所以现在降级使用;Camera2框架功能强大,对摄像头的控制灵活,但由于

    2023年04月13日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包