ZXing源码解析三:相机的配置与数据的处理2

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
简介: 相机的配置与数据的处理

设置相机预览图像的最佳比例

  文章前部分,已经分析了ZXing设置预览方向的代码,但是只设置预览方向还是不够的,还要根据屏幕的宽高比来找到相机采集图片最合适的预览尺寸,否则就会出现相机预览图拉伸变形的问题。继续看initFromCameraParameters方法中的代码,如下

 Point theScreenResolution = new Point();
    display.getSize(theScreenResolution);
    screenResolution = theScreenResolution;
    Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);
    cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
    Log.i(TAG, "Camera resolution: " + cameraResolution);
    bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
    Log.i(TAG, "Best available preview size: " + bestPreviewSize);
    boolean isScreenPortrait = screenResolution.x < screenResolution.y;
    boolean isPreviewSizePortrait = bestPreviewSize.x > bestPreviewSize.y;
    if (isScreenPortrait == isPreviewSizePortrait) {
      previewSizeOnScreen = bestPreviewSize;
    } else {
      previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);
    }

上面代码中的screenResolution变量是屏幕分辨率,从这个变量中可以分别获取屏幕宽高的像素值。我们来重点看下这两句代码

cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);

上面的一句代码是获取相机的最佳分辨率,下面的一句代码是获取获取相机的最佳预览尺寸。现在来看下是怎么获取最佳尺寸的,findBestPreviewSizeValue方法的代码如下

public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
    //获取相机支持的尺寸,手机不同会有不同的值
    List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
    if (rawSupportedSizes == null) {
      Log.w(TAG, "Device returned no supported preview sizes; using default");
      Camera.Size defaultSize = parameters.getPreviewSize();
      if (defaultSize == null) {
        throw new IllegalStateException("Parameters contained no preview size!");
      }
      return new Point(defaultSize.width, defaultSize.height);
    }
    if (Log.isLoggable(TAG, Log.INFO)) {
      StringBuilder previewSizesString = new StringBuilder();
      for (Camera.Size size : rawSupportedSizes) {
        previewSizesString.append(size.width).append('x').append(size.height).append(' ');
      }
      Log.i(TAG, "Supported preview sizes: " + previewSizesString);
    }
    //这句代码是获取屏幕宽高的比例
    double screenAspectRatio = screenResolution.x / (double) screenResolution.y;
    // Find a suitable size, with max resolution
    int maxResolution = 0;
    Camera.Size maxResPreviewSize = null;
    //for循环的作用是找到相机合适的尺寸和最大的分辨率,这里
    //合适的尺寸指的是和屏幕宽高比相同的尺寸。
    for (Camera.Size size : rawSupportedSizes) {
      int realWidth = size.width;
      int realHeight = size.height;
      int resolution = realWidth * realHeight;
      if (resolution < MIN_PREVIEW_PIXELS) {
        continue;
      }
      boolean isCandidatePortrait = realWidth < realHeight;
      int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
      int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
      double aspectRatio = maybeFlippedWidth / (double) maybeFlippedHeight;
      double distortion = Math.abs(aspectRatio - screenAspectRatio);
      if (distortion > MAX_ASPECT_DISTORTION) {
        continue;
      }
    //这句代码是找到与屏幕宽高比一致的尺寸,否则就用相机默认的尺寸
      if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
        Point exactPoint = new Point(realWidth, realHeight);
        Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
        return exactPoint;
      }
      // Resolution is suitable; record the one with max resolution
      if (resolution > maxResolution) {
        maxResolution = resolution;
        maxResPreviewSize = size;
      }
    }
    // If no exact match, use largest preview size. This was not a great idea on older devices because
    // of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
    // the CPU is much more powerful.
    if (maxResPreviewSize != null) {
      Point largestSize = new Point(maxResPreviewSize.width, maxResPreviewSize.height);
      Log.i(TAG, "Using largest suitable preview size: " + largestSize);
      return largestSize;
    }
    // If there is nothing at all suitable, return current preview size
    Camera.Size defaultPreview = parameters.getPreviewSize();
    if (defaultPreview == null) {
      throw new IllegalStateException("Parameters contained no preview size!");
    }
    Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
    Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
    return defaultSize;
  }

从上面代码中的注释可以看到这里存在一些小问题,上面代码的逻辑是有与屏幕像素比例相同的相机尺寸才返回,否则就用相机默认的尺寸,相机默认的尺寸可能与屏幕的尺寸比有较大的差距,这样就会出现预览图像变形的问题。

这里可以将代码优化为,返回最接近屏幕宽高比的相机尺寸。这里的优化将会在后面的文章中进行详细的讲解。

上面的代码是将一些变量的值设置好,最终,配置相机的参数在CameraConfigurationManager类中的setDesiredCameraParameters中,这里就不详细分析了。

旋转采集图片的方向

  这里没有处理采集的照片,采集到的照片数据还是横屏的,如下

这个图片是我竖屏时扫描的,但是获取相机采集的数据确是横屏的,所以,需要进行一些处理。 首先,需要在相机捕获图像数据成功的回调方法onPreviewFrame中改变代码,更改后的代码如下

@Override
  public void onPreviewFrame(byte[] data, Camera camera) {
    Point cameraResolution = configManager.getCameraResolution();
    Handler thePreviewHandler = previewHandler;
    if (cameraResolution != null && thePreviewHandler != null) {
      Point screenResolution = configManager.getScreenResolution();
      Message message;
      if (screenResolution.x < screenResolution.y){
        // 手机为竖屏时
        message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.y,
                cameraResolution.x, data);
      } else {
        // 手机为横屏时
        message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
                cameraResolution.y, data);
      }
      message.sendToTarget();
      previewHandler = null;
    } else {
      Log.d(TAG, "Got preview callback, but no handler or resolution available");
    }

解释:手机竖屏时,相机传感器采集的数据为横屏的数据,为了与竖屏相对应,需要将相机采集的图片宽高互换,这里只是互换了宽高,但是采集的数据宽高并没有转换,因此还需要将数据的宽高转换。

代码如下

 //将原始图像传感器的数据转换为竖屏
    if (width < height) {
      // portrait
      byte[] rotatedData = new byte[data.length];
      for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++)
          rotatedData[y * width + width - x - 1] = data[y + x * height];
      }
      data = rotatedData;
    }

将上面的代码,加入到DecodeHandler类中的decode方法开头即可。

虽然,这时已经将相机采集的横屏数据转化为竖屏的了,但是,工作还没有完成,还需要设置获取二维码的区域,设置的方法是CameraManager类中的getFramingRectInPrevie方法。这里我就补贴具体的代码了,大家根据前文的内容和自己的思考来修改里面的代码。

结束语

  文章主要分析了相机配置的代码,选择拍摄图像的最佳尺寸及处理相机采集到的数据,重点是要理解相机的数据采集与图像预览的设置。本篇修改的代码在这里

参考文章

Android: Camera相机开发详解(上) —— 知识储备

Android 相机预览需要注意的几点

相关文章
|
3天前
|
JSON 文字识别 数据格式
文本,文识11,解析OCR结果,paddOCR返回的数据,接口返回的数据有code,data,OCR返回是JSON的数据,得到JSON数据先安装依赖,Base64转换工具网站在21.14
文本,文识11,解析OCR结果,paddOCR返回的数据,接口返回的数据有code,data,OCR返回是JSON的数据,得到JSON数据先安装依赖,Base64转换工具网站在21.14
文本,文识11,解析OCR结果,paddOCR返回的数据,接口返回的数据有code,data,OCR返回是JSON的数据,得到JSON数据先安装依赖,Base64转换工具网站在21.14
|
3天前
|
存储 安全 Java
深度长文解析SpringWebFlux响应式框架15个核心组件源码
以上是Spring WebFlux 框架核心组件的全部介绍了,希望可以帮助你全面深入的理解 WebFlux的原理,关注【威哥爱编程】,主页里可查看V哥每天更新的原创技术内容,让我们一起成长。
|
4天前
|
关系型数据库 分布式数据库 数据库
PolarDB-X源码解析:揭秘分布式事务处理
【7月更文挑战第3天】**PolarDB-X源码解析:揭秘分布式事务处理** PolarDB-X,应对大规模分布式事务挑战,基于2PC协议确保ACID特性。通过预提交和提交阶段保证原子性与一致性,使用一致性快照隔离和乐观锁减少冲突,结合故障恢复机制确保高可用。源码中的事务管理逻辑展现了优化的分布式事务处理流程,为开发者提供了洞察分布式数据库核心技术的窗口。随着开源社区的发展,更多创新实践将促进数据库技术进步。
11 3
|
6天前
|
存储 JSON JavaScript
使用JSONObject解析与生成JSON数据
使用JSONObject解析与生成JSON数据
|
2天前
|
传感器 编解码 API
【STM32开发入门】温湿度监测系统实战:SPI LCD显示、HAL库应用、GPIO配置、UART中断接收、ADC采集与串口通信全解析
SPI(Serial Peripheral Interface)是一种同步串行通信接口,常用于微控制器与外围设备间的数据传输。SPI LCD是指使用SPI接口与微控制器通信的液晶显示屏。这类LCD通常具有较少的引脚(通常4个:MISO、MOSI、SCK和SS),因此在引脚资源有限的系统中非常有用。通过SPI协议,微控制器可以向LCD发送命令和数据,控制显示内容和模式。
|
3天前
Element UI 多选表格【翻页多选】全能版(含翻页多选数据反显、toggleRowSelection失效的原因解析和解决方案)
Element UI 多选表格【翻页多选】全能版(含翻页多选数据反显、toggleRowSelection失效的原因解析和解决方案)
9 0
|
4天前
|
前端开发 开发者
深入解析Vite.js源码
【7月更文挑战第1天】Vite.js 深入解析:以其无bundle开发、动态ES模块加载提升开发效率;本地HTTP服务器配合WebSocket实现热更新;按需加载减少资源占用;预构建优化生产环境性能;基于Rollup的插件系统增强灵活性。Vite,一个创新且高效的前端构建工具。
14 0
|
6天前
|
存储 缓存 安全
解析Java中的数据缓存技术
解析Java中的数据缓存技术
|
16天前
|
机器学习/深度学习 缓存 算法
netty源码解解析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk
netty源码解解析(4.0)-25 ByteBuf内存池:PoolArena-PoolChunk
|
18天前
|
XML Java 数据格式
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
深度解析 Spring 源码:从 BeanDefinition 源码探索 Bean 的本质
25 3

推荐镜像

更多