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


0 0
原创粉丝点击