【问题标题】:How to save the correct rotated image using camerax Front Camera?如何使用camerax前置摄像头保存正确的旋转图像?
【发布时间】:2022-03-11 01:21:49
【问题描述】:

我正在使用 CameraX 开发我的 android 应用程序,当我在横向模式或纵向模式下拍摄照片时,捕获并保存的图像是镜像。

我知道 FRONT 摄像头的工作方式相同。但是,如果我想以与拍摄时相同的方式保存照片,该怎么办?

这是我正在使用的buildUseCase() 代码:

private fun buildUseCases() {

        val screenAspectRatio = Rational(width, height)
        val screenTargetRotation = display.rotation

        //Preview
        val previewConfig = PreviewConfig.Builder().apply {
            setTargetAspectRatio(screenAspectRatio)
            setTargetRotation(screenTargetRotation)
            setLensFacing(lensFacing)
        }.build()

        preview = AutoFitPreviewBuilder.build(previewConfig, this)
        //End - Preview


        // Set up the capture use case to allow users to take photos
        val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
            setTargetAspectRatio(screenAspectRatio)
            setTargetRotation(screenTargetRotation)
            setLensFacing(lensFacing)
            setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY)
        }.build()


        imageCapture = ImageCapture(imageCaptureConfig)
    }

请帮助我更改以获取正确的图像。

注意:相机朝向正面且处于横向模式。

【问题讨论】:

    标签: android kotlin android-camerax


    【解决方案1】:

    您需要读取创建的图像的 EXIF 数据,并且必须根据要求和需要编写自己的自定义控制器。在大多数 Android 和 iOS 设备中,捕获的图像被旋转是很正常的,并且必须相应地进行处理。在大多数设备中,相机的默认方向设置为横向模式,因此即使您在纵向模式下拍照,它也会旋转 90 度。

    从 EXIF 数据中,您可以获得图像的旋转度数或镜像的程度,然后您可以在后端处理它。

    要旋转图像,您可以尝试

    private static Bitmap rotateImageIfRequired(Bitmap img, Uri selectedImage) throws IOException 
    {
        ExifInterface ei = new ExifInterface(selectedImage.getPath());
        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                return rotateImage(img, 90);
            case ExifInterface.ORIENTATION_ROTATE_180:
                return rotateImage(img, 180);
            case ExifInterface.ORIENTATION_ROTATE_270:
                return rotateImage(img, 270);
            default:
                return img;
        }
     }
    
    private static Bitmap rotateImage(Bitmap img, int degree)
    {
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
        img.recycle();
        return rotatedImg;
    }
    

    图片翻转问题可以试试这个

    public static Bitmap flip(Bitmap src, int type) 
    {
         // create new matrix for transformation
         Matrix matrix = new Matrix();
         matrix.preScale(-1.0f, 1.0f);
    
         // return transformed image
         return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
     }
    

    然后将图片设置为ImageView为

    imgPreview.setImageBitmap(flip(bitmap)); 
    

    【讨论】:

    • 嗨!有没有办法通过不保存图像来使用这个答案。换句话说:使用 ExifInterface 并从 ImageProxy 设置路径?如果我从这个答案中理解,您保存图像然后使用 Exif 获取有关旋转的信息,对吗?如果我只想拍照而不保存并且仍然遇到旋转问题怎么办?谢谢!
    • 使用 img.recycle(); 时要小心,因为我在项目中进一步使用了 src 位图,这给我带来了一个非常奇怪且难以跟踪的错误。我得到的错误是:2021-04-28 10:47:30.073 13573-15598/APPNAME A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x70 in tid 15598 (pool-7-thread-1), pid 13573 (ev.APPNAME )
    【解决方案2】:

    从 CameraX 团队的某个人那里收到了关于我自己代码中相同问题的建议。

    您的代码中缺少的是从 保存 Jpeg,以便您可以在 ImageView 上正确设置旋转。你 可以使用ExifInterface 执行此操作,或者仅使用库 为您处理此问题,例如Glide。官方样品, CameraXBasic,uses the Glide approach。

    首先,他们的提议意味着您无需为 Preview 或 ImageCapture 定义 setTargetRotation(screenTargetRotation) 即可生成以正确旋转显示的图像。

    如果官方示例发生变化,下面是 Glide 的使用方式:

    Glide.with().load().into()
    

    load() 接收对捕获图像的引用。如果您将其保存到 File,请提供对该 File 的引用。

    【讨论】:

      【解决方案3】:

      不是一个真正的答案,但如果您需要从 byte[] 而不是已经解压缩的位图读取 EXIF 信息,可能会非常方便。

       /**
       * Returns the degrees, counted clockwise, from a byte[] instead of a already saved file.<br>
       *
       * @param jpeg the JPEG image byte[]
       * @return Exif orientation as either <b>0</b>, <b>90</b>, <b>180</b> or <b>270</b>
       */
      public static int getExifOrientation(byte[] jpeg) {
          if (jpeg == null) {
              return 0;
          }
      
          int offset = 0;
          int length = 0;
      
          // ISO/IEC 10918-1:1993(E)
          while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
              int marker = jpeg[offset] & 0xFF;
      
              // Check if the marker is a padding.
              if (marker == 0xFF) {
                  continue;
              }
              offset++;
      
              // Check if the marker is SOI or TEM.
              if (marker == 0xD8 || marker == 0x01) {
                  continue;
              }
              // Check if the marker is EOI or SOS.
              if (marker == 0xD9 || marker == 0xDA) {
                  break;
              }
      
              // Get the length and check if it is reasonable.
              length = pack(jpeg, offset, 2, false);
              if (length < 2 || offset + length > jpeg.length) {
                  Log.e(TAG, "Invalid JPEG length");
                  return 0;
              }
      
              // Break if the marker is EXIF in APP1.
              if (marker == 0xE1 && length >= 8 &&
                      pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
                      pack(jpeg, offset + 6, 2, false) == 0) {
                  offset += 8;
                  length -= 8;
                  break;
              }
      
              // Skip other markers.
              offset += length;
              length = 0;
          }
      
          // JEITA CP-3451 Exif Version 2.2
          if (length > 8) {
              // Identify the byte order.
              int tag = pack(jpeg, offset, 4, false);
              if (tag != 0x49492A00 && tag != 0x4D4D002A) {
                  Log.e(TAG, "Invalid JPEG EXIF byte order");
                  return 0;
              }
              boolean littleEndian = (tag == 0x49492A00);
      
              // Get the offset and check if it is reasonable.
              int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
              if (count < 10 || count > length) {
                  Log.e(TAG, "Invalid JPEG EXIF offset");
                  return 0;
              }
              offset += count;
              length -= count;
      
              // Get the count and go through all the elements.
              count = pack(jpeg, offset - 2, 2, littleEndian);
              while (count-- > 0 && length >= 12) {
                  // Get the tag and check if it is orientation.
                  tag = pack(jpeg, offset, 2, littleEndian);
                  if (tag == 0x0112) {
                      // We do not really care about type and count, do we?
                      int orientation = pack(jpeg, offset + 8, 2, littleEndian);
                      switch (orientation) {
                          case 1:
                              return 0;
                          case 3:
                              return 180;
                          case 6:
                              return 90;
                          case 8:
                              return 270;
                      }
                      Log.i(TAG, "Unsupported EXIF orientation");
                      return 0;
                  }
                  offset += 12;
                  length -= 12;
              }
          }
      
          Log.i(TAG, "EXIF Orientation not found");
          return 0;
      }
      
      private static int pack(byte[] bytes, int offset, int length,
                              boolean littleEndian) {
          int step = 1;
          if (littleEndian) {
              offset += length - 1;
              step = -1;
          }
      
          int value = 0;
          while (length-- > 0) {
              value = (value << 8) | (bytes[offset] & 0xFF);
              offset += step;
          }
          return value;
      }
      

      【讨论】:

        【解决方案4】:

        为避免使用前置摄像头拍照时出现照片的镜面效果,需要将元数据传递给

        ImageCapture.OutputFileOptions

        根据使用的相机,这里是我所做的示例

                val metadata = ImageCapture.Metadata()
                metadata.isReversedHorizontal = cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA
        
                val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
                    .setMetadata(metadata)
                    .build()
        
               imageCapture.takePicture(outputOptions, executor, object: ImageCapture.OnImageSavedCallback {})
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-06
          • 1970-01-01
          • 2015-04-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多