【Android】开启前后相机用OpenCV进行实时人脸检测

90 篇文章 0 订阅

android中Camera setDisplayOrientation使用

Camera.parames.setDisplayOrientation问题解决

Android学习七---Hello OpenCV samples

Android学习——在Android中使用OpenCV的第一个程序

使用OpenCV Android SDK从摄像头帧实时检测人脸

[android]摄像机 setDisplayOrientation() 在肖像模式打破纵横比


detectMultiScale(
Mat image,             //输入图像
MatOfRect objects,     //检测到的Rect
double scaleFactor,    //缩放比例,必须大于1
int minNeighbors,      //合并窗口时最小neighbor,每个候选矩阵至少包含的附近元素个数
int flags,             //检测标记,只对旧格式的分类器有效,与cvHaarDetectObjects的参数flags相同,默认为0,可能的取值为CV_HAAR_DO_CANNY_PRUNING(CANNY边缘检测)、CV_HAAR_SCALE_IMAGE(缩放图像)、CV_HAAR_FIND_BIGGEST_OBJECT(寻找最大的目标)、CV_HAAR_DO_ROUGH_SEARCH(做粗略搜索);如果寻找最大的目标就不能缩放图像,也不能CANNY边缘检测
Size minSize,          //最小检测目标
Size maxSize           //最大检测目标
)


package com.duanjiwei.faceapp;

import android.app.Activity;
//import android.support.v7.app.AppCompatActivity;
//import android.content.Context;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.objdetect.CascadeClassifier;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2 {

    private CameraBridgeViewBase mOpenCvCameraView;
    private CascadeClassifier cascadeClassifier;
    private static final String TAG = "OCVSample::Activity";
    private boolean mIsFrontCamera = false;
    //ImageView picture;
    Mat imageMat;
    Mat mRgba;

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(MainActivity.this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    Log.i(TAG, "OpenCV loaded successfully");
                    imageMat=new Mat();

                    //加载人脸检测xml
                    try {
                        // Copy the resource into a temp file so OpenCV can load it
                        InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
                        File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
                        File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
                        FileOutputStream os = new FileOutputStream(mCascadeFile);

                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = is.read(buffer)) != -1) {
                            os.write(buffer, 0, bytesRead);
                        }
                        is.close();
                        os.close();

                        // Load the cascade classifier
                        cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath());
                    } catch (Exception e) {
                        Log.e("OpenCVActivity", "Error loading cascade", e);
                    }

                    //enable camera
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };

    public MainActivity() {
        Log.i(TAG, "Instantiated new " + MainActivity.this.getClass());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //picture   = (ImageView)findViewById(R.id.imageView);
        mOpenCvCameraView = (CameraBridgeViewBase)findViewById(R.id.java_surface_view);
        mOpenCvCameraView.setCvCameraViewListener(MainActivity.this);

        Button button1 = (Button)findViewById(R.id.button);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "开启前置相机!", Toast.LENGTH_SHORT).show();
                mOpenCvCameraView.setVisibility(SurfaceView.GONE);

                //前置相机
                mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.java_surface_view);
                mOpenCvCameraView.setCameraIndex(1);

                mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
                mOpenCvCameraView.setCvCameraViewListener(MainActivity.this);
                mOpenCvCameraView.enableView();
                mIsFrontCamera = true;
            }
        });

        Button button2 = (Button)findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "开启后置相机!!", Toast.LENGTH_SHORT).show();
                mOpenCvCameraView.setVisibility(SurfaceView.GONE);

                //后置相机
                mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.java_surface_view);
                mOpenCvCameraView.setCameraIndex(-1);

                mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
                mOpenCvCameraView.setCvCameraViewListener(MainActivity.this);
                mOpenCvCameraView.enableView();
                mIsFrontCamera = false;
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item){
        switch(item.getItemId()){
            case R.id.add_item:
                Toast.makeText(this, "You Clicked Add!!", Toast.LENGTH_SHORT).show();
                break;
            case R.id.remove_item:
                Toast.makeText(this, "You Clicked Remove!", Toast.LENGTH_SHORT).show();
                break;
            case R.id.delete_item:
                Toast.makeText(this, "You Clicked Delete!", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
        return true;
    }



    @Override
    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d("OpenCV", "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, this, mLoaderCallback);
        } else {
            Log.d("OpenCV", "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    @Override
    public void onCameraViewStarted(int width, int height) {
        mRgba  = new Mat(height, width, CvType.CV_8UC4);
    }

    @Override
    public void onCameraViewStopped() {
        mRgba.release();
    }

    @Override
    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        Mat mGray = inputFrame.gray();
        Mat mShow;
        mShow = inputFrame.rgba();
        if ( !mIsFrontCamera)
        {
            Core.flip(mShow, mShow, 1);
        }

        //检测并显示
        MatOfRect faces = new MatOfRect();
        if (cascadeClassifier != null) {
            cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 2, 2, new Size(mGray.height()/5, mGray.height()/5), new Size());
        }

        Rect[] facesArray = faces.toArray();
        for (int i = 0; i <facesArray.length; i++)
            Core.rectangle(mShow, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3);
        return mShow;
    }


    @Override
    public void onPause()
    {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

}



<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.duanjiwei.camcapture.MainActivity">

    <org.opencv.android.JavaCameraView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/java_surface_view"
        android:layout_marginBottom="279dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id = "@+id/add_item"
        android:title = "Add"/>
    <item
        android:id = "@+id/remove_item"
        android:title = "Remove"/>
    <item
        android:id = "@+id/delete_item"
        android:title = "Delete"/>
</menu>


相机权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.duanjiwei.camcapture">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>


    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
    <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>



</manifest>





[android]摄像机 setDisplayOrientation() 在肖像模式打破纵横比


我想拿相机预览,在纵向模式中正常工作允许活动本身通常改变方向的位置 (即,不被锁定为横向)。

使用 setDisplayOrientation() 严重打破的预告片的行为。

这可以表明对谷歌的 ApiDemos。第一幅图像基于 CameraPreview 从 android-17 版的 ApiDemos 示例项目,哪里的唯一的更改是删除android:orientation="landscape" 从清单中的这一活动的项。下面的图像是纸的什么的结点 4,运行 Android 4.2,指着一个 8.5"正方形的相机显示屏上显示的截图:

Nexus 4 ApiDemos Preview, Portrait Mode

不从,预览可以明显看出的是它是旋转 90 度。你看到在最右边的图像的袜子包脚实际上低于广场。

为此,解决方案至少为横向模式,是使用 setDisplayOrientation() 。但是,如果您修改 Preview 的内部类 CameraPreview 有mCamera.setDisplayOrientation(90); ,你拿到这个:

Nexus 4 ApiDemos Preview, Portrait Mode, with setDisplayOrientation(90)

值得注意的是,在广场不再是方形的。

这将使用相同 SurfaceView 以前一样大小。我以外的再现了这种情况在一些其他的代码, ApiDemos ,和我获取相同的行为与 TextureView 中的代码。

我简要地以为这个问题可能是, getSupportedPreviewSizes() 可能会返回不同的值,基于 setDisplayOrientation() ,但快速的测试表明这不是个案。

任何人都不知道如何获得 setDisplayOrientation() ,没有破坏图像的长宽比推到预览在纵向模式下工作吗?

谢谢 !

解决方法 1:

好的我想说搞这清楚。有几件谚语难题,并感谢 @kcoppock 帮我解决的这一些对洞察。

首先,你需要考虑到方向时确定哪些预览的大小来选择。例如, getOptimalPreviewSize() 从 CameraPreview 的 ApiDemos 是漠视了他们的方向,只是因为他们的那个应用程序的版本已锁定为横向的方向。如果您想要让方向浮动,你必须扭转目标纵横比,以匹配。因此,凡getOptimalPreviewSize() 有:

double targetRatio=(double)width / height;

您还需要:

if (displayOrientation == 90 || displayOrientation == 270) {
  targetRatio
=(double)height / width;
}

其中 displayOrientation 是一个从 0 到 360,我很确定从大约 100 行一些严重丑陋的代码,这就是为什么我将结束这一切在不久就会发布一个可重用组件的值。代码基于 setDisplayOrientation() JavaDocs这个计算器答案

(BTW,如果你读到这在 2013 年 7 月 1 日之后,你看不到这个答案在该组件中的链接,发表评论,提醒我,因为我忘了)

第二,你需要考虑到那显示方向控制的纵横比时 SurfaceView / TextureView 使用的。CameraPreview活动从 ApiDemos 有自己的 PreviewViewGroup ,处理纵横比,和你那里需要扭转在肖像使用纵横比:

    if (displayOrientation == 90
       
|| displayOrientation == 270) {
      previewWidth
=mPreviewSize.height;
      previewHeight
=mPreviewSize.width;
   
}
   
else {
      previewWidth
=mPreviewSize.width;
      previewHeight
=mPreviewSize.height;
   
}

哪里 displayOrientation 那是相同的值 ( 90 和 270 分别被反向肖像和纵向和我还没试越来越反向纵向或反向景观要工作,所以可能会有更多的调整所需的注)。

第三 — — 和我发现令人愤怒 — — 你必须开始预览之前调用一个 setPictureSize() 上 Camera.Parameters 。否则,它是因为如果图片的纵横比应用到预览帧,事情搞砸。

所以我用了类似于以下内容的代码:

Camera.Parameters parameters=camera.getParameters();
Camera.Size pictureSize=getHost().getPictureSize(parameters);

parameters
.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
parameters
.setPictureSize(pictureSize.width, pictureSize.height);
parameters
.setPictureFormat(ImageFormat.JPEG);

camera
.setParameters(parameters);
camera
.startPreview();

这是错误的。你真正需要的是:

Camera.Parameters parameters=camera.getParameters();
Camera.Size pictureSize=getHost().getPictureSize(parameters);

parameters
.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

camera
.setParameters(parameters);
camera
.startPreview();

parameters
=camera.getParameters();
parameters
.setPictureSize(pictureSize.width, pictureSize.height);
parameters
.setPictureFormat(ImageFormat.JPEG);
camera
.setParameters(parameters);

没有这种变化,我舒展的纵横比,甚至在景观。这不是一个问题为 CameraPreview 的 ApiDemos ,因为他们都不在拍照和所以他们永远不会调用setPictureSize() 。

不过,到目前为止,Nexus 4、 我现在有方形长宽比为纵向和横向浮动的方向 (即,不锁定为横向)。

我会试着记住要修正这个问题,如果我跑进了其他黑客所需的其他设备,再加上要链接到我 CameraView / CameraFragment 组件时他们被释放。


更新 #1

嗯,当然,它不可能由这个简单。:-)

问题的修复程序在 setPictureSize() 螺丝了纵横比作品......直到你拍张照片。此时,预览切换到错的纵横比。

一种可能性就是限制图片对那些具有相同 (或非常接近) 纵横比为预览,所以呃逆并不存在。我不喜欢这个答案,用户应该能够拿相机的任何图片大小提供。

您可以限制所损害的范围:

  • 只更新图片大小等的 Camera.Parameters 只是在拍摄照片之前
  • 恢复前 picture 考虑到 Camera.Parameters 后拍摄照片

这在虽然图片正在采取期间时刻仍然构成了错的纵横比。长远的解决办法,这个--我想--将暂时用静止图像 (最后一个预览帧) 同时图片替换相机预览正在采取。我最终来试试这个。



  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值