Android 手机Camera Orientation问题的深入研究
来源:互联网 发布:java入门视频百度网盘 编辑:程序博客网 时间:2024/04/30 14:08
在Android 开发过程中经常跟Camera 打交道,对Camera Orientation 的理解由开始的迷糊到精通,着实走了不少弯路。本文将对Android 系统Camera 相关经验做一个总结。
1. 手机的屏幕画面显示的自然方向:所谓自然方向就是人体手持手机时,视觉的习惯方向。例如,竖屏手机的自然方向一般都是垂直短边框向上,横屏手机的自然方向一般都是垂直长边框向上。这个自然方向是手机出厂时就定义好了的,相当于定义了一个坐标系,所有的旋转都要参照这个坐标系。
2. 本文提及的所有角度都是顺时针方向旋转,即以参照物为起点,目标物为终点,顺时针走过的角度即为目标物与参照物之间的夹角。
3. 当手机旋转时,display.getRotation() 返回的值为当前手机的空间位置与手机自然朝向之间的夹角。例如:将竖屛手机向右倾倒横放,display.getRotation() = 90;将竖屛手机向左倾倒横放,display.getRotation() = 270;将竖屛手机上下颠倒放置,display.getRotation() = 180。
4. 不管是横屏还是竖屛手机,Camera 的物理方向一般都是垂直于长边框向上。也就说对于竖屏手机,其Camera 物理方向与屏幕Display的自然方向的夹角为90度(顺时针)如下图一;对于横屏手机,其Camera 自然方向与屏幕Display的自然方向的夹角为0度,如下图二。
5. Camera 拍照时,总是按照Camera 的物理方向取景,即相当于将人的脑袋朝着Camera 的物理方向所看到的视角。对于横屏手机,当手横持手机拍照时,拍出来的照片即为用户眼前所见视角。而对于竖屛手机,当手竖持手机拍照时,拍出的照片往往旋转了90度,这是因为在竖屛手机上,Camera 本身物理方向就与屏幕Display的自然方向有90度夹角,用手竖持手机时,Camera的方向是水平向右的,如下图一。因此要想没有90度旋转,需要设置 Camera.setDisplayOrientation(90) 来校正。
6. 如何计算当前Camera 物理位置与屏幕Display自然方向间的夹角?
算法的核心思想是:先假设手机为竖屛手机,然后按照竖屛手机的角度关系计算Camera 物理位置与屏幕Display自然方向间的夹角,最后根据长宽关系判断当前手机是否为竖屛,如果不是,补偿一个270度然后对360取模,非常巧妙。
public static int getOrientation(Context context) { Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); int rotation = display.getRotation(); int orientation; boolean expectPortrait; switch (rotation) { case Surface.ROTATION_0: default: orientation = 90; expectPortrait = true; break; case Surface.ROTATION_90: orientation = 0; expectPortrait = false; break; case Surface.ROTATION_180: orientation = 270; expectPortrait = true; break; case Surface.ROTATION_270: orientation = 180; expectPortrait = false; break; } boolean isPortrait = display.getHeight() > display.getWidth(); if (isPortrait != expectPortrait) { orientation = (orientation + 270) % 360; } return orientation; }
7.如何为Surface View 设置一个最佳的尺寸?
private Size calculateBestPreviewSizeAndAdjustLayout(Camera camera, Rect surfaceSize, int orientation, int minHeight, int maxHeight) { SizeAndRatio optimalSizeAndRatio = null; // output double targetRatio = (double)surfaceSize.right/surfaceSize.bottom; if (orientation % 180 == 90) { // We are in portrait targetRatio = 1/targetRatio; m_updatedCameraFrameLayoutSize = new Point(surfaceSize.bottom,surfaceSize.right); } else { m_updatedCameraFrameLayoutSize = new Point(surfaceSize.right,surfaceSize.bottom); } boolean allowedResize = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH && orientation != 270; List<Size> supportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes(); // Collect sub-list that meets height requirements List<SizeAndRatio> cameraSizesAndRatios = new ArrayList<SizeAndRatio>(); for (Size s : supportedPreviewSizes) { if (s.height < minHeight || s.height > maxHeight) { continue; // Filter out too small or too large sizes } double ratio = ((double) s.width) / s.height; SizeAndRatio sr = new SizeAndRatio(s, ratio); cameraSizesAndRatios.add(sr); } // Error, no available sizes if (cameraSizesAndRatios.isEmpty()) { return null; } // Sort the list by height descending Collections.sort(cameraSizesAndRatios, new Comparator<SizeAndRatio>() { @Override public int compare(SizeAndRatio a_lhs, SizeAndRatio a_rhs) { return (a_rhs.size.height) - (a_lhs.size.height); } }); // First try to find an exact screen aspect ratio match for (SizeAndRatio sr : cameraSizesAndRatios) { if (Math.abs(sr.ratio - targetRatio) < EPSILON_TOLERANCE) { optimalSizeAndRatio = sr; break; } } if (optimalSizeAndRatio != null) { return optimalSizeAndRatio.size; } // If we allow layout resizing, try to find 16:9 or 4:3 ratio if (allowedResize) { // Try to find a 16:9 ratio for (SizeAndRatio sr : cameraSizesAndRatios) { if (Math.abs(sr.ratio - RATIO16_9) < EPSILON_TOLERANCE) { optimalSizeAndRatio = sr; break; } } if (optimalSizeAndRatio != null) {<p class="p1"> // Notify that the screen layout should change</p><p class="p2"> <span class="s1">m_updatedCameraFrameLayoutSize</span> = adjustSurfaceSize(<span class="s1">mActivity</span>, optimalSizeAndRatio.<span class="s1">ratio</span>, orientation);</p> return optimalSizeAndRatio.size; } // Try to find a 4:3 ratio for (SizeAndRatio sr : cameraSizesAndRatios) { if (Math.abs(sr.ratio - RATIO4_3) < EPSILON_TOLERANCE) { optimalSizeAndRatio = sr; break; } } if (optimalSizeAndRatio != null) {<p class="p1"><span class="s1"> </span>// Notify that the screen layout should change</p><p class="p2"> <span class="s2">m_updatedCameraFrameLayoutSize</span> = adjustSurfaceSize(<span class="s2">mActivity</span>, optimalSizeAndRatio.<span class="s2">ratio</span>, orientation);</p> return optimalSizeAndRatio.size; } } // Now just use preview size with smallest ratio difference double minRatioDifference = Double.MAX_VALUE; for (SizeAndRatio sr : cameraSizesAndRatios) { double ratioDifference = Math.abs(sr.ratio - targetRatio); if (ratioDifference < minRatioDifference) { minRatioDifference = ratioDifference; optimalSizeAndRatio = sr; } }<p class="p1"> </p><p class="p1"><span class="s1"> if</span> (allowedResize) {</p><p class="p1"> <span class="s2">m_updatedCameraFrameLayoutSize</span> = adjustSurfaceSize(<span class="s2">mActivity</span>, optimalSizeAndRatio.<span class="s2">ratio</span>, orientation);</p><p class="p1"> }</p> return optimalSizeAndRatio.size; }
public Point adjustSurfaceSize(FSECameraActivity activity, double a_aspectRatio, int a_orientation) { boolean isPortrait = a_orientation % 180 == 90; // Collect the width, height, and ratio of the screen Display display = activity.getWindowManager().getDefaultDisplay(); int windowWidth, windowHeight; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { Point size = new Point(); display.getSize(size); windowWidth = size.x; windowHeight = size.y; } else { // Old APIs windowWidth = display.getWidth(); windowHeight = display.getHeight(); } int leftMargin = 0; // This may need to be adjusted for increasing width in portrait double currentRatio = (double)windowWidth / (double)windowHeight; // Adjust the ratios if (currentRatio < 1 && a_aspectRatio > 1 || currentRatio > 1 && a_aspectRatio < 1) { a_aspectRatio = 1.0/a_aspectRatio; } // Adjust the necessary dimension if (currentRatio > a_aspectRatio) { // If current ratio is larger, we need to increase the smaller dimension (height) windowHeight = (int) (windowWidth / a_aspectRatio); } else if (currentRatio < a_aspectRatio) { // If current ratio is smaller, we need to increase the larger dimension (width) int oldWindowWidth = windowWidth; windowWidth = (int) (windowHeight * a_aspectRatio); if (isPortrait) { // We also need to fix the Left Margin if in portrait leftMargin = oldWindowWidth - windowWidth; } } else if (currentRatio == a_aspectRatio) { // No adjustment needed } // Update layout parameters final LayoutParams lp = (FrameLayout.LayoutParams) mPreviewLayout.getLayoutParams(); lp.width = windowWidth; lp.height = windowHeight; lp.topMargin = 0; lp.leftMargin = leftMargin; mCameraScreenMarginLeft = leftMargin; mPreviewLayout.setLayoutParams(lp); // Return a point where X corresponds to Camera Width, and Y corresponds to Camera Height. return isPortrait ? new Point(windowHeight, windowWidth) : new Point(windowWidth, windowHeight); }
8. 如何将Camera 捕景框上的点坐标转化为手机屏幕上的坐标?
/** * Translate a point returned by <span style="font-family: Arial, Helvetica, sans-serif;">camera sensor </span>into a point on the camera surface preview * @param point input Point returned from <span style="font-family: Arial, Helvetica, sans-serif;">camera sensor</span> * @return PointF */ public PointF translatePoint(PointF point) { switch (mOrientation) { case 0: // landscape right return new PointF(point.x*m_updatedCameraFrameLayoutSize.x/mCameraPreviewSize.width, point.y*m_updatedCameraFrameLayoutSize.y/mCameraPreviewSize.height); case 180: // landscape left return new PointF((mCameraPreviewSize.width-point.x)*m_updatedCameraFrameLayoutSize.x/mCameraPreviewSize.width, (mCameraPreviewSize.height-point.y)*m_updatedCameraFrameLayoutSize.y/mCameraPreviewSize.height); case 270: // upside down portrait return new PointF(point.y*m_updatedCameraFrameLayoutSize.y/mCameraPreviewSize.height, (mCameraPreviewSize.width-point.x)*m_updatedCameraFrameLayoutSize.x/mCameraPreviewSize.width); default: // 90, normal portrait return new PointF((mCameraPreviewSize.height-point.y)*m_updatedCameraFrameLayoutSize.y/mCameraPreviewSize.height, point.x*m_updatedCameraFrameLayoutSize.x/mCameraPreviewSize.width); } }
- Android 手机Camera Orientation问题的深入研究
- [Android Camera]关于Android Camera Orientation的一些总结
- 关于Android的Orientation问题
- camera orientation
- How to set Android camera orientation properly?
- android:configChanges="orientation"使用中应该注意的问题
- android camera研究
- android camera研究
- android camera 研究学习
- Android:Camera的使用,并处理手机拍照后上传图片被旋转的问题
- Android Camera setRecordingHint(true)函数 在部分手机上的问题,例如拉伸,变形
- android:用Camera拍照,解决某些手机利用自带相机崩溃的问题
- android手机字体库相关问题深入剖析
- android Camera 小研究 (一)
- android Camera 小研究 (二)
- android:orientation
- android camera获取matrix作用于canvas的研究
- Android Camera系统深入理解
- 羆膁葿蚁羅芄芁薇肄羃蒇蒃
- 衿膅肇蚆袁芁芆蒆螄袄莅莅
- 2015.3.17图书馆的管理系统
- 螀肈芀蚄袃袀膆蚃薂肆肂蚂
- 蚁羆膂螀螇膅莄蚂螂袁芄蚅
- Android 手机Camera Orientation问题的深入研究
- 肁芄薇袆袄膀薆薆聿肅薆蚈
- 蒃蚀螅芄艿葿螇芀莈螁螀羃
- 虿肂芁蚂薅肂莄蒅袃肁肃芇
- 袂肇肄薂螁聿腿蚀蚁羂芅蒇
- 芇薆袆衿芆蚈蝿膈芅莈羅肄
- 虿聿蒇螇薂膅芃薆羇膀莂蒅
- 袄芄薀薄羆肇蒆薃聿芃莂蚃
- FragmentTabHost切换Fragment时避免重复加载UI