将相机预览旋转到肖像Android OpenCV相机

我正在尝试使用OpenCV 2.4.3.2创build一个摄像头的应用程序,并做一些opencv处理。 我希望能够拥有多个UI方向,而不仅仅是Landscape。

问题是,当我改变方向为肖像,图像出来侧面。

我知道我可以在进行image processing之前旋转input图像 (因此只将方向设置为横向),这很好,并且可以正常工作,但是并不能解决我的其他用户界面错误的问题。

我也尝试使用此代码来旋转相机90deg,但它似乎并没有工作。

mCamera.setDisplayOrientation(90); 

它要么没有效果,要么有时会导致预览被遮挡

有没有人用OpenCV成功完成这个任务? 我的类从JavaCameraView扩展。 纵向图像与侧面预览

编辑

我做了一个改进,就是我在OpenCV里面旋转了图像,就像在CameraBridgeViewBase.java类中显示的那样。

在交付和绘制框架方法中:

 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()); 

…基本上,这只是像这样roatatesinput图像

输入图像旋转90

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

Solutions Collecting From Web of "将相机预览旋转到肖像Android OpenCV相机"

我有同样的问题试图实现OpenCV。 我能够通过对deliverAndDrawFrame方法进行以下更改来修复它。

  1. 旋转canvas对象

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

     // 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); 

其实,你可以做宽度或高度math亲(全屏)。

 if (canvas != null) { canvas.rotate(90,0,0); 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); 

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

我修改了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比较慢,因为计算开销较大。

不幸的是Opencv4Android不支持纵向相机。 但是有一个办法可以解决这个问题。 1)写你的自定义相机,并将其方向设置为肖像。 2)注册它的预览callback。 3)在onPreviewFrame(byte[]data, Camera camera)创build预览字节的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。 不要忘了释放完成后创build的所有Mat实例。

PPS。 在一个单独的线程上pipe理你的相机是很好的,以便在进行一些检测时不会超出UI线程。

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

在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()); 

看起来像新的OpenCV CameraBridgeViewBase.java类是太高层次,并没有给予足够的控制相机预览的布局。 看看我的示例代码 ,它基于一些较旧的OpenCV示例,并使用纯Android代码。 要使用onPreviewFrame传递的字节数组, put()其放到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); 

您可能能够在互联网上find旧的OpenCV4Android样本,尽pipe它们在几个版本之前被取出。 但是,链接的示例代码和上面的代码段应该足以让您开始。

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

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

希望能帮助到你 !!!

我用CameraBridgeViewBase获得了纵向,但是我不得不改变OpenCV中的JavaCameraView.java :(接下来的想法是:在camera init之后,下一步

 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) { } } 

你必须考虑一些事情:

  • onPreviewFrame()总是以原有的旋转方式传送原始摄像机数据
  • getSupportedPreviewSizes()给出了相应的宽高比
  • algorithm需要分析纵向帧来检测对象是否正确。
  • 创build(Java方)来存储结果帧的位图也需要正确的宽高比

所以,对于一个快速和高分辨率的解决scheme,我改变了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); 

有了这些改变,我们确保我们正在使用可用于纵向的最高parsing度(在calculateCameraFrameSize中切换高度/宽度)。 我们仍然处理从onPreviewFrame()的input,但创build一个位图绘制肖像(AllocateCache)。

最后,我们需要给algorithm提供纵向框架,以便让他检测“站立”的对象,并返回它来保存和渲染位图。 所以下面的修改你的活动:

 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); ... 

“jaiprakashgogi”开发人员的答案正在为我工​​作。 但问题是预览仍然保存为横向只。 这意味着如果我们将预览设置为imageview,那么它将显示为横向。

上面的解决scheme可以将预览显示为肖像,但不能永久保存为肖像。

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

  1. 将字节或垫数据转换为位图
  2. 将matrix旋转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; } 

它完全解决了我的问题。

我有同样的问题,我已经弄清楚了! 并有我的解决scheme:

作为第一部分,在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(); 

}

那么,您需要如下replace函数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); } } 

}

并添加此function额外,

 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; } 

}

这是okey,如果这个答案对你有用,请标记“接受”的帮助信誉

你不需要旋转你的屏幕。

使用以下行编辑您的Manifest.xml文件:

  <activity android:screenOrientation="landscape" android:configChanges="keyboardHidden|orientation|screenSize"> 

这将提供全屏相机预览,其工作方式与系统相机预览相同。