• Android OpenCV竖屏处理策略和底层代码修改


    在Android上做机器视觉,不可避免地都会用到OpenCV for Android库,视频流的捕获和预览通常使用 JavaCamera2View 控件,但这个控件在横屏下运作良好,竖屏下问题很多。

    安卓手机只有在横屏下,其镜头传感器才是原始状态,竖屏需要进行旋转,否则看起来还是横的。

    为了保持旋转不变性,最佳策略是采用正方形的预览区域,有些手机的镜头的尺寸不一定有正方形的输出尺寸,比如以vivo Y32为例,屏幕的分辨率是1600x720,镜头并没有720x720的输出,这种情况下我们可以找到一个最接近的960x720,然后将输出剪裁为720x720。

    下面详细描述修改要点。

    首先,修改 CameraBridgeViewBase.java,增加一个成员变量:

    1. // 正方形边长,大于0表示竖屏,将屏幕显示区域改为正方形,方便任意旋转
    2. public int mSideLengthSquare = 0; // 0则为横屏,不需要额外处理

    这个判断在 JavaCamera2View.java文件完成:

    1. protected boolean connectCamera(int width, int height) {
    2. Log.i(LOGTAG, "PreviewSize(" + width + "x" + height + ")");
    3. startBackgroundThread();
    4. initializeCamera();
    5. try {
    6. // 判断是否竖屏的逻辑
    7. if(width < height){ // 原始预览区域的宽度比高度小,说明是竖屏
    8. mSideLengthSquare = width;
    9. }
    10. ...
    11. }
    12. ...
    13. }

    修改文件的CameraBridgeViewBase.java文件的calculateCameraFrameSize()函数:

    1. if(mSideLengthSquare > 0){ // 如果是竖屏
    2. // 找出最接近正方形边长的输出尺寸:
    3. int minDx = 1024*100;
    4. for (Object size : supportedSizes) {
    5. int width = accessor.getWidth(size);
    6. int height = accessor.getHeight(size);
    7. Log.d(TAG, "trying size 0: " + width + "x" + height);
    8. if(width >= mSideLengthSquare && height >= mSideLengthSquare){
    9. int dx = (width - mSideLengthSquare) + (height - mSideLengthSquare);
    10. if(dx < minDx){
    11. calcWidth = (int) width;
    12. calcHeight = (int) height;
    13. minDx = dx;
    14. }
    15. }
    16. }
    17. }
    18. else {
    19. ...
    20. }

    注意在调用计算尺寸后,还要将缩放比例设为0,我们正方形区域没必要缩放,如果不设置下面这个语句,将会缩小显示:

    mScale = 0;

    修改CameraBridgeViewBase.java文件的AllocateCache()函数,如忘记修改将会崩溃:

    1. protected void AllocateCache()
    2. {
    3. Log.i(TAG, "AllocateCache size: " + mFrameWidth + "x" + mFrameHeight);
    4. // mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
    5. if(mSideLengthSquare > 0) { // 如果是竖屏,则输出的是正方形预览
    6. mCacheBitmap = Bitmap.createBitmap(mSideLengthSquare, mSideLengthSquare, Bitmap.Config.ARGB_8888);
    7. }
    8. else{
    9. mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
    10. }
    11. }

    然后是修改CameraBridgeViewBase.java文件的deliverAndDrawFrame()函数:

    1. protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
    2. Mat modified;
    3. if (mListener != null) {
    4. modified = mListener.onCameraFrame(frame);
    5. } else {
    6. modified = frame.rgba();
    7. }
    8. if(mSideLengthSquare > 0) { // 将帧画面剪裁为正方形
    9. int w0 = modified.cols();
    10. int h0 = modified.rows();
    11. if (w0 != mSideLengthSquare || h0 != mSideLengthSquare) {
    12. int x0 = 0;
    13. int y0 = 0;
    14. if (w0 > mSideLengthSquare) {
    15. x0 = (w0 - mSideLengthSquare) / 2;
    16. w0 = mSideLengthSquare;
    17. }
    18. if (h0 > mSideLengthSquare) {
    19. y0 = (h0 - mSideLengthSquare) / 2;
    20. h0 = mSideLengthSquare;
    21. }
    22. org.opencv.core.Rect rect = new org.opencv.core.Rect(x0, y0, w0, h0);
    23. modified = new Mat(modified, rect);
    24. }
    25. }
    26. ...
    27. }

    最后,则是你自己的C++代码了,别忘了将画面Mat旋转90度,这个是最容易的部分,就不贴代码了。

    上述代码具有极大的通用性,不需要设置尺寸而取最大的正方形,是一个很好的策略。用很多手机测试过,效果很棒,CPU占用极小,可以保持30+的帧率不变。

  • 相关阅读:
    使用docker搭建nacos单机、集群 + mysql
    对象实例、类信息、常量、静态变量分别在运行时数据区的哪个位置?
    设计模式_模板方法模式
    1x1卷积的作用
    springboot导入excel(POI)
    JDBC编程
    2020华数杯全国大学生数学建模竞赛C题-基于大数据对脱贫帮扶绩效的评价(一)(附带赛题解析&获奖论文及MATLAB代码)
    iOS小技能:Xcode13的使用技巧
    DNA偶联二维过渡金属硫化物|DNA修饰贵金属纳米颗粒|使用方法
    Hystrix原理
  • 原文地址:https://blog.csdn.net/bluesen/article/details/125545660