【问题标题】:Rotate camera preview to Portrait Android OpenCV Camera将相机预览旋转到人像 Android OpenCV 相机
【发布时间】:2013-01-26 19:00:54
【问题描述】:

我正在尝试使用 OpenCV 2.4.3.2 创建相机应用程序并进行一些 opencv 处理。我希望它能够有多个 UI 方向,而不仅仅是横向。

问题是当我将方向更改为纵向时,图像会侧向显示。

我了解I could just rotate the input image 在进行图像处理之前(因此仅将方向保留为横向),这很好并且有效,但不能解决我的 UI 的其余部分将处于错误方向的问题。

我也尝试使用this code 将相机旋转 90 度,但它似乎不起作用。

mCamera.setDisplayOrientation(90);

它要么没有效果,要么有时只是导致预览变黑

有人用 OpenCV 成功地做到了这一点吗?我的课程从 JavaCameraView 扩展而来。

编辑

我做了一个改进,就是我在 OpenCV 中旋转了图像,因为它显示在 CameraBridgeViewBase.java 类中。

在deliver和draw frame方法中:

if (canvas != null) {
            canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
            //canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight() - mCacheBitmap.getHeight()) / 2, null);
            //Change to support portrait view
            Matrix matrix = new Matrix();
            matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,(canvas.getHeight() - mCacheBitmap.getHeight()) / 2);

            if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
                matrix.postRotate(90f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
            canvas.drawBitmap(mCacheBitmap, matrix, new Paint());

... 基本上,这只是像这样旋转输入图像

这样更好,但我显然希望这是全屏的。

【问题讨论】:

  • 为什么不在Android上简单地设置屏幕方向?
  • 我只是添加了一张图片来解释这个行为,我之前解释得不够好。我实际上将方向设置为纵向
  • 您的代码是基于 opencv android 示例的吗?我强烈建议您在其中一个示例之上构建您的代码,因为它的代码已经修复了您将来可能遇到的相机问题。
  • 哪个样本?我想我已经尝试了所有这些,它们似乎都是一样的(而且是错误的)
  • @ruimarques 另外,期望的行为不是它只是保持纵向(尽管有时可以,但这不是我想要的)

标签: android opencv android-camera


【解决方案1】:

我在尝试实现 OpenCV 时遇到了同样的问题。我能够通过对 DeliverAndDrawFrame 方法进行以下更改来修复它。

  1. 旋转画布对象

    Canvas canvas = getHolder().lockCanvas();
    // Rotate canvas to 90 degrees
    canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2);
    
  2. 在绘制之前调整位图大小以适应画布的整个大小

    // Resize
    Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
    // Use bitmap instead of mCacheBitmap
    canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect(
        (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2),
        (int)((canvas.getHeight() - mScale*bitmap.getHeight()) / 2),
        (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2 + mScale*bitmap.getWidth()),
        (int)((canvas.getHeight() - mScale*bitmap.getHeight()) / 2 + mScale*bitmap.getHeight()
      )), null);
    
    // Unlock canvas
    getHolder().unlockCanvasAndPost(canvas);
    

【讨论】:

  • Wrboleski,问题是当您使用 FeatureDetector 时,这不起作用。特征检测器现在从侧面查看图像。
  • 这会在我运行相机时崩溃。
  • 适应屏幕canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()), new Rect(-(canvas.getHeight() - canvas.getWidth()) / 2, (canvas.getHeight() - canvas.getWidth()) / 2, (canvas.getHeight() - canvas.getWidth()) / 2 + canvas.getWidth(), canvas.getHeight() - (canvas.getHeight() - canvas.getWidth()) / 2), null);
  • @KelvinNg 这是有效的,但图像是以低分辨率捕获的。如何改善?
【解决方案2】:

我修改CameraBridgeViewBase.java如下:

protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
    int calcWidth = 0;
    int calcHeight = 0;

    if(surfaceHeight > surfaceWidth){
        int temp = surfaceHeight;
        surfaceHeight = surfaceWidth;
        surfaceWidth = temp;
    }

并且在函数“deliverAndDrawFrame”中:

            if (mScale != 0) {
                if(canvas.getWidth() > canvas.getHeight()) {
                canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                     new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
                     (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
                     (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                     (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
                } else {
                    canvas.drawBitmap(mCacheBitmap, rotateMe(canvas, mCacheBitmap), null);
                }

其中rotateMe定义如下:

private Matrix rotateMe(Canvas canvas, Bitmap bm) {
    // TODO Auto-generated method stub
    Matrix mtx=new Matrix();
    float scale = (float) canvas.getWidth() / (float) bm.getHeight();
    mtx.preTranslate((canvas.getWidth() - bm.getWidth())/2, (canvas.getHeight() - bm.getHeight())/2);
    mtx.postRotate(90,canvas.getWidth()/2, canvas.getHeight()/2);
    mtx.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
    return mtx;
}

预览 FPS 较慢,因为与横向模式相比,计算开销更大。

【讨论】:

  • 这很好用。但是我正在使用的人脸检测示例不再识别任何人脸。它假定横向模式。有什么想法吗?谢谢
  • 相机在纵向模式下会变慢。并且横向模式对双方都不起作用
  • 这行得通,但正如你所说,fps 会急剧下降,这会达到目的。有没有一种有效的方法来实现类似的结果?
【解决方案3】:

实际上,您可以只使宽度或高度匹配父级(全屏)。

if (canvas != null) {
        Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
        canvas.rotate(90,0,0);
        float scale = canvas.getWidth() / (float)bitmap.getHeight();
        float scale2 = canvas.getHeight() / (float)bitmap.getWidth();
        if(scale2 > scale){
            scale = scale2;
        }
        if (scale != 0) {
            canvas.scale(scale, scale,0,0);
        }
        canvas.drawBitmap(bitmap, 0, -bitmap.getHeight(), null);

...

此外,您可以使预览尺寸大于屏幕。只需修改比例即可。

【讨论】:

    【解决方案4】:

    不幸的是 Opencv4Android 不支持人像相机。但是有一种方法可以克服它。 1)编写您的自定义相机并将其方向设置为纵向。 2)注册它的预览回调。 3)在onPreviewFrame(byte[]data, Camera camera)中创建Mat的预览字节:

    Mat mat = new Mat(previewSize.height, previewSize.width, CvType.CV_8UC1);
    mat.put(0, 0, data);
    
    Core.transpose(mat, mat);
    Core.flip(mat, mat, -1); // rotates Mat to portrait
    

    CvType 取决于您的相机使用的预览格式。

    PS。完成后不要忘记释放您创建的所有 Mat 实例。

    PPS。最好在单独的线程上管理您的相机,以免在进行某些检测时使 UI 线程过载。

    【讨论】:

      【解决方案5】:

      我也有同样的问题,已经解决了!!还有我的解决方案:

      作为第一部分,在CameraBridgeViewBase.Java的两个构造函数中,添加WindowManager的初始化:

      public CameraBridgeViewBase(Context context, int cameraId) {  
         super(context);  
         mCameraIndex = cameraId;  
         getHolder().addCallback(this);  
         mMaxWidth = MAX_UNSPECIFIED;  
         mMaxHeight = MAX_UNSPECIFIED;  
         windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
      }  
      
      
      public CameraBridgeViewBase(Context context, AttributeSet attrs) {  
         super(context, attrs);  
         int count = attrs.getAttributeCount();  
         Log.d(TAG, "Attr count: " + Integer.valueOf(count));  
      
         TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs, R.styleable.CameraBridgeViewBase);  
         if (styledAttrs.getBoolean(R.styleable.CameraBridgeViewBase_show_fps, false))  
             enableFpsMeter();  
      
         mCameraIndex = styledAttrs.getInt(R.styleable.CameraBridgeViewBase_camera_id, -1);  
      
         getHolder().addCallback(this);  
         mMaxWidth = MAX_UNSPECIFIED;  
         mMaxHeight = MAX_UNSPECIFIED;  
         windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);  
         styledAttrs.recycle();  
      }  
      

      那么,你需要替换函数deliverAndDrawFrame(CvCameraViewFrame frame)如下,

      protected void deliverAndDrawFrame(CvCameraViewFrame frame) {  
        Mat modified;  
      
        if (mListener != null) {  
            modified = mListener.onCameraFrame(frame);  
        } else {  
            modified = frame.rgba();  
        }  
      
        boolean bmpValid = true;  
        if (modified != null) {  
            try {  
                Utils.matToBitmap(modified, mCacheBitmap);  
            } catch (Exception e) {  
                Log.e(TAG, "Mat type: " + modified);  
                Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());  
                Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());  
                bmpValid = false;  
            }  
        }  
      
        if (bmpValid && mCacheBitmap != null) {  
            Canvas canvas = getHolder().lockCanvas();  
            if (canvas != null) {  
                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);  
                int rotation = windowManager.getDefaultDisplay().getRotation();  
                int degrees = 0;  
                // config degrees as you need  
                switch (rotation) {  
                    case Surface.ROTATION_0:  
                        degrees = 90;  
                        break;  
                    case Surface.ROTATION_90:  
                        degrees = 0;  
                        break;  
                    case Surface.ROTATION_180:  
                        degrees = 270;  
                        break;  
                    case Surface.ROTATION_270:  
                        degrees = 180;  
                        break;  
                }  
      
                Matrix matrix = new Matrix();  
                matrix.postRotate(degrees);  
                Bitmap outputBitmap = Bitmap.createBitmap(mCacheBitmap, 0, 0, mCacheBitmap.getWidth(), mCacheBitmap.getHeight(), matrix, true);  
      
                if (outputBitmap.getWidth() <= canvas.getWidth()) {  
                    mScale = getRatio(outputBitmap.getWidth(), outputBitmap.getHeight(), canvas.getWidth(), canvas.getHeight());  
                } else {  
                    mScale = getRatio(canvas.getWidth(), canvas.getHeight(), outputBitmap.getWidth(), outputBitmap.getHeight());  
                }  
      
                if (mScale != 0) {  
                    canvas.scale(mScale, mScale, 0, 0);  
                }  
                Log.d(TAG, "mStretch value: " + mScale);  
      
                canvas.drawBitmap(outputBitmap, 0, 0, null);  
      
                if (mFpsMeter != null) {  
                    mFpsMeter.measure();  
                    mFpsMeter.draw(canvas, 20, 30);  
                }  
                getHolder().unlockCanvasAndPost(canvas);
            }  
         }  
      }  
      

      并额外添加此功能,

      private float getRatio(int widthSource, int heightSource, int widthTarget, int heightTarget) {  
         if (widthTarget <= heightTarget) {  
             return (float) heightTarget / (float) heightSource;  
         } else {  
             return (float) widthTarget / (float) widthSource;  
         }  
      }  
      

      没关系,如果这个答案对你有用,请标记“已接受”帮助声誉

      【讨论】:

      • 我在访问 deliverAndDrawFrame 中的 windowManager 时遇到错误,即使它位于构造函数中。除非我说WindowManager windowManager = ...,否则我不能毫无错误地声明 windowManager
      【解决方案6】:

      这里的所有答案都是黑客。我更喜欢这个解决方案:

      JavaCameraView 代码更改:

      mBuffer = new byte[size];
      mCamera.setDisplayOrientation(90); //add this
      mCamera.addCallbackBuffer(mBuffer);
      

      第二次改变:

      //                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
      //                        mSurfaceTexture = new SurfaceTexture(MAGIC_TEXTURE_ID);
      //                        mCamera.setPreviewTexture(mSurfaceTexture);
      //                    } else
      //                       mCamera.setPreviewDisplay(null);
                          mCamera.setPreviewDisplay(getHolder());
      

      【讨论】:

      • 这会移除绿色人脸检测框
      • 此解决方案仅用于预览图像,但在其上绘制的任何内容或任何类型的对象检测都不起作用。
      • 这也分解了任何类型处理的显示结果,例如自适应过滤...等。
      【解决方案7】:

      与其他答案一样,我编写了个人版本的 deliverAndDrawFrame(我还通过 cmets 通知了我的代码开始和结束的位置):

      protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
          Mat modified;
      
          if (mListener != null) {
              modified = mListener.onCameraFrame(frame);
          } else {
              modified = frame.rgba();
          }
      
          boolean bmpValid = true;
          if (modified != null) {
              try {
                  Utils.matToBitmap(modified, mCacheBitmap);
              } catch(Exception e) {
                  Log.e(TAG, "Mat type: " + modified);
                  Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
                  Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
                  bmpValid = false;
              }
          }
      
          if (bmpValid && mCacheBitmap != null) {
              Canvas canvas = getHolder().lockCanvas();
              if (canvas != null) {
                  canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                  if (BuildConfig.DEBUG) {
                      Log.d(TAG, "mStretch value: " + mScale);
                  }
      
                  // Start of the fix
                  Matrix matrix = new Matrix();
                  matrix.preTranslate( ( canvas.getWidth() - mCacheBitmap.getWidth() ) / 2f, ( canvas.getHeight() - mCacheBitmap.getHeight() ) / 2f );
                  matrix.postRotate( 90f, ( canvas.getWidth()) / 2f, canvas.getHeight() / 2f );
                  float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
                  matrix.postScale(scale, scale, canvas.getWidth() / 2f , canvas.getHeight() / 2f );
                  canvas.drawBitmap( mCacheBitmap, matrix, null );
      
                  // Back to original OpenCV code
                  if (mFpsMeter != null) {
                      mFpsMeter.measure();
                      mFpsMeter.draw(canvas, 20, 30);
                  }
      
                  getHolder().unlockCanvasAndPost(canvas);
              }
          }
      
      }
      

      预览现在处于纵向模式,如您所见:

      【讨论】:

      • 嗨@Roses 你在屏幕分辨率上工作过吗?您的代码工作正常,但图片分辨率在我的分辨率为 480*864 时保持不变,但我的设备支持的不止于此,如何解决这个问题?
      【解决方案8】:

      新的 OpenCV CameraBridgeViewBase.java 类似乎太高级了,无法对相机预览的布局提供足够的控制。看看我的sample code,它基于一些较旧的 OpenCV 示例并使用纯 Android 代码。使用onPreviewFrameput()传入的字节数组将其转换为Mat并从YUV转换为RGB:

      mYuv = new Mat(previewHeight + previewHeight/2, previewWidth, CvType.CV_8UC1);
      mYuv.put(0, 0, mBuffer);
      Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGBA, 4);
      

      您可能可以在互联网上找到旧的 OpenCV4Android 示例,尽管它们是在几个版本之前取出的。但是,链接的示例代码和上面的 sn-p 应该足以让您入门。

      【讨论】:

        【解决方案9】:

        如果您使用的是 openCV 2.4.9,请尝试: 1)将opencv教程混合处理的内容复制到你的代码中; 2)纠正不匹配的错误(活动名称和可能的布局参考); 3)通过添加android:screenOrientation ="landscape"修改您的清单 4)纠正未成年人错误并运行!!!! bbaamm(现在应该可以正常工作了)

        注意:使用此方法,当手机处于纵向位置时,状态栏会出现在右侧。 由于我们正在开发相机项目,我建议您从预览中删除状态栏。

        希望对你有帮助!!!

        【讨论】:

          【解决方案10】:

          你必须考虑一些事情:

          • onPreviewFrame() 始终在其组装的旋转中提供原始相机数据
          • getSupportedPreviewSizes() 给出相应的纵横比
          • 算法需要分析纵向框架以正确检测对象。
          • 为存储结果帧而创建的位图(Java 端)也需要正确的纵横比

          因此,为了获得快速和高分辨率的解决方案,我更改了 JavaCameraView.java 和我的 JNI 部分。 在 JavaCameraView.java 中:

          ...
          
                if (sizes != null) {
                   /* Select the size that fits surface considering maximum size allowed */
                   Size frameSize;
                   if(width > height)
                   {
                      frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height);
                   }else{
                      frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), height, width);
                   }
          ...
          
                   mCamera.setParameters(params);
                   params = mCamera.getParameters();
          
                   int bufFrameWidth, bufFrameHeight;
                   bufFrameWidth = params.getPreviewSize().width;
                   bufFrameHeight = params.getPreviewSize().height;
          
                   if(width > height) {
                       mFrameWidth = params.getPreviewSize().width;
                       mFrameHeight = params.getPreviewSize().height;
                   }else{
                       mFrameWidth = params.getPreviewSize().height;
                       mFrameHeight = params.getPreviewSize().width;
                   }
          ...
          
                   mFrameChain = new Mat[2];
                   mFrameChain[0] = new Mat(bufFrameHeight + (bufFrameHeight/2), bufFrameWidth, CvType.CV_8UC1);
                   mFrameChain[1] = new Mat(bufFrameHeight + (bufFrameHeight/2), bufFrameWidth, CvType.CV_8UC1);
          
                   AllocateCache();
          
                   mCameraFrame = new JavaCameraFrame[2];
                   mCameraFrame[0] = new JavaCameraFrame(mFrameChain[0], bufFrameWidth, bufFrameHeight);
                   mCameraFrame[1] = new JavaCameraFrame(mFrameChain[1], bufFrameWidth, bufFrameHeight);
          

          通过这些更改,我们确保使用可用于纵向的最高结果(在 calculateCameraFrameSize 中切换高度/宽度)。我们仍然将横向作为 onPreviewFrame() 的输入处理,但创建了一个位图以纵向绘制 (AllocateCache)。

          最后,我们需要为算法提供纵向框架,以便让他检测“站立”对象并将其返回以保存和渲染位图。 因此,对您的 Activity 进行以下修改:

          public Mat rot90(Mat matImage, int rotflag){
              //1=CW, 2=CCW, 3=180
              Mat rotated = new Mat();
              if (rotflag == 1){
                  rotated = matImage.t();
                  flip(rotated, rotated, 1); //transpose+flip(1)=CW
              } else if (rotflag == 2) {
                  rotated = matImage.t();
                  flip(rotated, rotated,0); //transpose+flip(0)=CCW
              } else if (rotflag ==3){
                  flip(matImage, rotated,-1);    //flip(-1)=180
              } else if (rotflag != 0){ //if not 0,1,2,3:
                 Log.e(TAG, "Unknown rotation flag("+rotflag+")");
              }
              return rotated;
          }
          
          public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
          
              mRgba = rot90(inputFrame.rgba(), 1);
              mGray = rot90(inputFrame.gray(), 1);
          ...
          

          【讨论】:

            【解决方案11】:

            我已经使用 CameraBridgeViewBase 进行纵向定位,但我必须在 OpenCV 中更改 JavaCameraView.java :( 下一步是:在相机初始化后,下一步

            setDisplayOrientation(mCamera, 90);
            mCamera.setPreviewDisplay(getHolder());
            

            和setDisplayOrientation方法

            protected void setDisplayOrientation(Camera camera, int angle){
                Method downPolymorphic;
                try
                {
                    downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
                    if (downPolymorphic != null)
                        downPolymorphic.invoke(camera, new Object[] { angle });
                }
                catch (Exception e1)
                {
                }
            }
            

            【讨论】:

            • 嗨,我也面临方向问题。可以更详细地描述您的解决方案吗?我试图添加 (JavaCamerView) setDisplayOrientation(mCamera, 90); mCamera.setPreviewDisplay(getHolder());在 if (!initializeCamera(width, height)) 返回 false 之后;但是,这会导致黑色预览屏幕。
            • 此方法为我纠正了方向,但预览回调停止工作,并且它还不断抛出以下异常 -
            • E/SurfaceHolder:异常锁定表面 java.lang.IllegalArgumentException
            【解决方案12】:

            “jaiprakashgogi”开发人员回答对我有用。但问题是预览仍然只保存为横向。这意味着如果我们将预览设置为 imageview,那么它将显示为横向。

            上述解决方案可以将预览显示为纵向,但不会永久保存为纵向。

            我通过以下方式解决了这个问题。

            1. 将字节或mat数据转换为位图
            2. 将矩阵旋转90度并应用于位图
            3. 将位图转换为字节数组并保存。

            请在此处查看我的代码...

             public String writeToSDFile(byte[] data, int rotation){
            
            
                byte[]  portraitData=null;
            
               if(rotation==90){
                   Log.i(TAG,"Rotation is : "+rotation);
                   Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length);
                   Matrix matrix = new Matrix();
            
                   matrix.postRotate(90);
            
                   Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
               portraitData=bitmapToByte(rotatedBitmap);
            
            
               }
            
                File dir=getDirectory();
                String imageTime=""+System.currentTimeMillis();
            
                String fileName=Constants.FILE_NAME+imageTime+"."+Constants.IMAGE_FORMAT;
                File file = new File(dir, fileName);
            
                try {
                    FileOutputStream f = new FileOutputStream(file);
            
                    if(rotation==90){
                        f.write(portraitData);
                    }else {
                        f.write(data);
                    }
            
                    f.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                    Log.i(TAG, "******* File not found. Did you" +
                            " add a WRITE_EXTERNAL_STORAGE permission to the   manifest?");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.i(TAG,"\n\nFile written to "+file);
            
                return fileName;
            }
            
             // convert bitmap to Byte Array
            
              public byte[] bitmapToByte(Bitmap bitmap){
            
                ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
              bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
            
                byte[] array=outputStream.toByteArray();
               return array;
            }
            

            彻底解决了我的问题。

            【讨论】:

              【解决方案13】:

              感谢@Kaye Wrobleski 的回答。 我已将其扩展为允许横向和纵向。 这基本上只是一些额外的代码,可以轻松地在提供横向方向的默认代码和他的纵向代码之间切换。

              将他的代码作为新方法插入到 CameraBridgeViewBase.java 中

              protected void deliverAndDrawFramePortrait(CvCameraViewFrame frame) {
                      Mat modified;
              
                      if (mListener != null) {
                          modified = mListener.onCameraFrame(frame);
                      } else {
                          modified = frame.rgba();
                      }
              
                      boolean bmpValid = true;
                      if (modified != null) {
                          try {
                              Utils.matToBitmap(modified, mCacheBitmap);
                          } catch(Exception e) {
                              Log.e(TAG, "Mat type: " + modified);
                              Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
                              Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
                              bmpValid = false;
                          }
                      }
              
                      if (bmpValid && mCacheBitmap != null) {
                          Canvas canvas = getHolder().lockCanvas();
                          // Rotate canvas to 90 degrees
                          canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2);
                          if (canvas != null) {
                              canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                              Log.d(TAG, "mStretch value: " + mScale);
              
                              if (mScale != 0) {
                                  // Resize
                                  Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
                                  // Use bitmap instead of mCacheBitmap
                                  canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect(
                                          (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2),
                                          (int)((canvas.getHeight() - mScale*bitmap.getHeight()) / 2),
                                          (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2 + mScale*bitmap.getWidth()),
                                          (int)((canvas.getHeight() - mScale*bitmap.getHeight()) / 2 + mScale*bitmap.getHeight())), null);
                              } else {
                                  Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
                                  // Use bitmap instead of mCacheBitmap
                                  canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect(
                                          (int)((canvas.getWidth() - bitmap.getWidth()) / 2),
                                          (int)((canvas.getHeight() - bitmap.getHeight()) / 2),
                                          (int)((canvas.getWidth() - bitmap.getWidth()) / 2 + bitmap.getWidth()),
                                          (int)((canvas.getHeight() - bitmap.getHeight()) / 2 + bitmap.getHeight())), null);
                              }
              
                              if (mFpsMeter != null) {
                                  mFpsMeter.measure();
                                  mFpsMeter.draw(canvas, 20, 30);
                              }
                              getHolder().unlockCanvasAndPost(canvas);
                          }
                      }
                  }
              

              然后修改JavaCameraView.java

              添加一个新变量来跟踪我们是处于纵向还是横向模式

              private boolean portraitMode;

              增加两种设置方向模式的方法

              public void setLandscapeMode() {
                      portraitMode = false;
                  }
                  public void setPortraitMode() {
                      portraitMode = true;
                  }
              

              现在在 JavaCameraView CameraWorkerClass, run() 方法中替换这些行

              if (!mFrameChain[1 - mChainIdx].empty())
                                      deliverAndDrawFrame(mCameraFrame[1 - mChainIdx]);
              

              这些行:

              if (!mFrameChain[1 - mChainIdx].empty()) {
                                      if (!portraitMode) {
                                          deliverAndDrawFrame(mCameraFrame[1 - mChainIdx]);
                                      } else {
                                          deliverAndDrawFramePortrait(mCameraFrame[1 - mChainIdx]);
                                      }
                                  }
              

              要在方向之间切换,只需在 JavaCameraView 对象上调用 setLandscapeMode() 或 setPortraitMode()。

              请注意,反向纵向和反向横向仍然会颠倒。 您需要将它们旋转 180 度以使它们正面朝上,这可以通过 OpenCV 的 warpAffine() 方法轻松完成。 请注意,使用后置摄像头(LENS_FACING_BACK)时,人像模式会将图像上下翻转。

              【讨论】:

                【解决方案14】:

                也许这对任何人都有帮助。使用 opencv343 在 android 9 上测试。现在这个全屏和检测人脸在纵向和横向模式下。 CameraBridgeViewBase 类的小改动:

                private final Matrix matrix = new Matrix();
                

                …… 更改deliverAndDrawFrame() 方法:

                protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
                    Mat modified;
                
                    if (mListener != null) {
                        modified = mListener.onCameraFrame(frame);
                    } else {
                        modified = frame.rgba();
                    }
                
                    boolean bmpValid = true;
                    if (modified != null) {
                        try {
                            Utils.matToBitmap(modified, mCacheBitmap);
                        } catch(Exception e) {
                            Log.e(TAG, "Mat type: " + modified);
                            Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
                            Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
                            bmpValid = false;
                        }
                    }
                
                    if (bmpValid && mCacheBitmap != null) {
                        int currentOrientation = getResources().getConfiguration().orientation;
                        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
                            Canvas canvas = getHolder().lockCanvas();
                            if (canvas != null) {
                                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                                if (BuildConfig.DEBUG)
                                    Log.d(TAG, "mStretch value: " + mScale);
                
                                if (mScale != 0) {
                                    canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                            new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
                                                    (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
                                                    (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                                                    (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
                
                                } else {
                                    canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                            new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
                                                    (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
                                                    (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
                                                    (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
                                }
                
                                if (mFpsMeter != null) {
                                    mFpsMeter.measure();
                                    mFpsMeter.draw(canvas, 20, 30);
                                }
                                getHolder().unlockCanvasAndPost(canvas);
                            }
                        } else {
                            Canvas canvas = getHolder().lockCanvas();
                            if (canvas != null) {
                                int saveCount = canvas.save();
                                canvas.setMatrix(matrix);
                                mScale = Math.max((float) canvas.getHeight() / mCacheBitmap.getWidth(), (float) canvas.getWidth() / mCacheBitmap.getHeight());
                                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                
                                if (mScale != 0) {
                                    canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                            new Rect((int)((canvas.getWidth() - mCacheBitmap.getWidth()) - mCacheBitmap.getWidth())/2,
                                                    (int)(canvas.getHeight() - mScale*mCacheBitmap.getHeight() - mScale*mCacheBitmap.getHeight()/2),
                                                    (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                                                    (int)((canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
                
                                } else {
                                    canvas.drawBitmap(mCacheBitmap, new Rect(0, 0, mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                            new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
                                                    (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
                                                    (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
                                                    (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
                                }
                                canvas.restoreToCount(saveCount);
                
                                if (mFpsMeter != null) {
                                    mFpsMeter.measure();
                                    mFpsMeter.draw(canvas, 20, 30);
                                }
                                getHolder().unlockCanvasAndPost(canvas);
                            }
                        }
                    }
                }
                

                在 MainActivity 中:

                public Mat rotateMat(Mat matImage) {
                    Mat rotated = matImage.t();
                    Core.flip(rotated, rotated, 1);
                    return rotated;
                }
                
                @Override
                public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
                    MatOfRect faces = new MatOfRect();
                    int currentOrientation = getResources().getConfiguration().orientation;
                    if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
                        mRgba = inputFrame.rgba();
                        mGray = inputFrame.gray();
                        int height = mGray.rows();
                        if (Math.round(height * 0.2) > 0) {
                            mFaceSize = (int) Math.round(height * 0.2);
                        }
                
                        cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2,
                                new Size(mFaceSize, mFaceSize));
                        Rect[] facesArray = faces.toArray();
                        for (int i = 0; i < facesArray.length; i++) {
                            rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
                        }
                    } else {
                
                        mRgba = inputFrame.rgba();
                        mGray = rotateMat(inputFrame.gray());
                
                        if (mFaceSize == 0) {
                            int height = mGray.cols();
                            if (Math.round(height * 0.2) > 0) {
                                mFaceSize = (int) Math.round(height * 0.2);
                            }
                        }
                        Mat newMat = rotateMat(mRgba);
                        if(!isBackCameraOn){
                            flip(newMat, newMat, -1);
                            flip(mGray, mGray, -1);
                        }
                        if (cascadeClassifier != null)
                            cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2, new Size(mFaceSize, mFaceSize));
                        mGray.release();
                
                        Rect[] facesArray = faces.toArray();
                        for (int i = 0; i < facesArray.length; i++) {
                            rectangle(newMat, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
                        }
                
                        Imgproc.resize(newMat, mRgba, new Size(mRgba.width(), mRgba.height()));
                
                        newMat.release();
                    }
                
                    if(!isBackCameraOn){
                        flip(mRgba, mRgba, 1);
                        flip(mGray, mGray, 1);
                    }
                
                
                    return mRgba;
                }
                

                【讨论】:

                  【解决方案15】:

                  另一种解决方案。我觉得这个更好

                  protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
                      Mat modified;
                  
                      if (mListener != null) {
                          modified = mListener.onCameraFrame(frame);
                      } else {
                          modified = frame.rgba();
                      }
                  
                      boolean bmpValid = true;
                      if (modified != null) {
                          try {
                              Utils.matToBitmap(modified, mCacheBitmap);
                          } catch(Exception e) {
                              Log.e(TAG, "Mat type: " + modified);
                              Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
                              Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
                              bmpValid = false;
                          }
                      }
                  
                      if (bmpValid && mCacheBitmap != null) {
                          Canvas canvas = getHolder().lockCanvas();
                          if (canvas != null) {
                              canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                              if (BuildConfig.DEBUG)
                                  Log.d(TAG, "mStretch value: " + mScale);
                  
                              int currentOrientation = getResources().getConfiguration().orientation;
                              if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
                                  if (mScale != 0) {
                                      canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                              new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
                                                      (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
                                                      (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                                                      (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
                                  } else {
                                      canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                              new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
                                                      (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
                                                      (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
                                                      (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
                                  }
                              } else {
                  
                                  if (mScale != 0) {
                                      Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
                                      canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect(
                                              (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2),
                                              (int)(0),
                                              (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2 + mScale*bitmap.getWidth()),
                                              (int)((canvas.getHeight()))), null);
                                  } else {
                                      Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
                                      canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect(
                                              (int)((canvas.getWidth() - bitmap.getWidth()) / 2),
                                              (int)(0),
                                              (int)((canvas.getWidth() - bitmap.getWidth()) / 2 + bitmap.getWidth()),
                                              (int)(canvas.getHeight())), null);
                                  }
                              }
                  
                              if (mFpsMeter != null) {
                                  mFpsMeter.measure();
                                  mFpsMeter.draw(canvas, 20, 30);
                              }
                              getHolder().unlockCanvasAndPost(canvas);
                          }
                      }
                  }
                  

                  还有……

                  @Override
                  public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
                      MatOfRect faces = new MatOfRect();
                      int currentOrientation = getResources().getConfiguration().orientation;
                      if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) { 
                          mRgba = inputFrame.rgba();
                          mGray = inputFrame.gray();
                  
                          int height = mGray.rows();
                          if (Math.round(height * 0.2) > 0) {
                              mFaceSize = (int) Math.round(height * 0.2);
                          }
                  
                          cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2,
                                  new Size(mFaceSize, mFaceSize));
                          Rect[] facesArray = faces.toArray();
                          for (int i = 0; i < facesArray.length; i++) {
                              Point center = new Point(facesArray[i].x + facesArray[i].width / 2,
                                      facesArray[i].y + facesArray[i].height / 2);
                              rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
                          }
                  
                      } else {
                          mRgba = inputFrame.rgba();
                          mGray = inputFrame.gray();
                  
                          Mat rotImage = Imgproc.getRotationMatrix2D(new Point(mRgba.cols() / 2,
                                  mRgba.rows() / 2), 90, 1.0);
                  
                          Imgproc.warpAffine(mRgba, mRgba, rotImage, mRgba.size());
                          Imgproc.warpAffine(mGray, mGray, rotImage, mRgba.size());
                  
                          Core.flip(mRgba, mRgba, 1);
                          Core.flip(mGray, mGray, 1);
                  
                          int height = mGray.rows();
                          if (Math.round(height * 0.2) > 0) {
                              mFaceSize = (int) Math.round(height * 0.2);
                          }
                  
                          cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2,
                                  new Size(mFaceSize, mFaceSize));
                          Rect[] facesArray = faces.toArray();
                          for (int i = 0; i < facesArray.length; i++) {
                              Point center = new Point(facesArray[i].x + facesArray[i].width / 2,
                                      facesArray[i].y + facesArray[i].height / 2);
                              rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
                          }
                      }
                  
                      return mRgba;
                  

                  【讨论】:

                    【解决方案16】:

                    我不认为有办法做到这一点,没有一些像素操作。但是,如果我们简单地修改所有这些像素被绘制到的矩阵。答案部分在于 CameraBridgeViewBase.java 文件

                    1.转到 CameraBridgeViewBase 类

                    2。制作函数更新矩阵

                    private final Matrix mMatrix = new Matrix();
                    private void updateMatrix() {
                    float mw = this.getWidth();
                    float mh = this.getHeight();
                    
                    float hw = this.getWidth() / 2.0f;
                    float hh = this.getHeight() / 2.0f;
                    
                    float cw  = (float)Resources.getSystem().getDisplayMetrics().widthPixels; //Make sure to import Resources package
                    float ch  = (float)Resources.getSystem().getDisplayMetrics().heightPixels;
                    
                    float scale = cw / (float)mh;
                    float scale2 = ch / (float)mw;
                    if(scale2 > scale){
                        scale = scale2;
                    }
                    
                    boolean isFrontCamera = mCameraIndex == CAMERA_ID_FRONT;
                    
                    mMatrix.reset();
                    if (isFrontCamera) {
                        mMatrix.preScale(-1, 1, hw, hh); //MH - this will mirror the camera
                    }
                    mMatrix.preTranslate(hw, hh);
                    if (isFrontCamera){
                        mMatrix.preRotate(270);
                    } else {
                        mMatrix.preRotate(90);
                    }
                    mMatrix.preTranslate(-hw, -hh);
                    mMatrix.preScale(scale,scale,hw,hh);
                    }
                    

                    3.覆盖 onMeasure 和布局函数

                    @Override
                    public void layout(int l, int t, int r, int b) {
                      super.layout(l, t, r, b);
                      updateMatrix();
                    }
                    
                    @Override
                    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                      updateMatrix();
                    }
                    

                    4.替换现有的 DeliverAndDrawFrame 函数

                    protected void deliverAndDrawFrame(CvCameraViewFrame frame) { //replaces existing deliverAndDrawFrame
                    Mat modified;
                    
                    if (mListener != null) {
                        modified = mListener.onCameraFrame(frame);
                    } else {
                        modified = frame.rgba();
                    }
                    
                    boolean bmpValid = true;
                    if (modified != null) {
                        try {
                            Utils.matToBitmap(modified, mCacheBitmap);
                        } catch(Exception e) {
                            Log.e(TAG, "Mat type: " + modified);
                            Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
                            Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
                            bmpValid = false;
                        }
                    }
                    
                    if (bmpValid && mCacheBitmap != null) {
                        Canvas canvas = getHolder().lockCanvas();
                        if (canvas != null) {
                            canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                            int saveCount = canvas.save();
                            canvas.setMatrix(mMatrix);
                    
                            if (mScale != 0) {
                                canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                        new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
                                                (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
                                                (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                                                (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
                            } else {
                                canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                                        new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
                                                (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
                                                (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
                                                (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
                            }
                    
                            //Restore canvas after draw bitmap
                            canvas.restoreToCount(saveCount);
                    
                            if (mFpsMeter != null) {
                                mFpsMeter.measure();
                                mFpsMeter.draw(canvas, 20, 30);
                            }
                            getHolder().unlockCanvasAndPost(canvas);
                        }
                    }
                    }
                    

                    【讨论】:

                      【解决方案17】:

                      经过大量搜索,我找到了这个-

                      https://answers.opencv.org/question/23972/face-detect-with-portrait-mode-and-front-camera/

                      这行得通。

                      另外,请确保您在 AndroidManifest.xml 中设置了纵向模式

                          android:screenOrientation="portrait"
                      

                      【讨论】:

                        【解决方案18】:

                        我不太清楚,但相机大小是由屏幕宽度决定的。由于屏幕宽度低,相机高度也由纵向的低决定。因此,相机分辨率也由低决定。并放好预览图(预览图旋转由CameraBridgeViewBase.java中相机图像的宽高决定)。

                        作为解决方案,使用横向(将 manifest.xml 中的横向模式决定为 Activity)。结果,因为屏幕宽度很高,所以高度也会很高,你的应用决定了高分辨率。此外,您不必旋转相机图像并始终全屏模式。但缺点是原点不同。我尝试了将高分辨率图像作为纵向方向的各种方法,但我找不到方法。

                        我的应用:纵向方向

                        我的相机图像是 720、480 / 横向 1280、1080。

                        【讨论】:

                          【解决方案19】:

                          修改JavaCameraView.java 中的代码,如this page 所述

                          这真的很容易修复。

                          之前

                          Log.d(TAG, "startPreview");
                          
                          mCamera.startPreview();
                          

                          之后

                          Log.d(TAG, "startPreview");
                          
                          setDisplayOrientation(mCamera, 90);
                          
                          mCamera.setPreviewDisplay(getHolder());
                          
                          mCamera.startPreview();
                          

                          【讨论】:

                            猜你喜欢
                            • 2017-02-11
                            • 2016-01-05
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-01-11
                            • 1970-01-01
                            • 2021-05-20
                            • 1970-01-01
                            • 2012-11-09
                            相关资源
                            最近更新 更多