您的位置:首页 > 移动开发 > Android开发

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

2016-08-17 16:40 701 查看


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"正方形的相机显示屏上显示的截图:



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

为此,解决方案至少为横向模式,是使用 
setDisplayOrientation()
 。但是,如果您修改 
Preview
 的内部类 
CameraPreview
 有
mCamera.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
 有自己的 
Preview
ViewGroup
 ,处理纵横比,和你那里需要扭转在肖像使用纵横比:
    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
 后拍摄照片

这在虽然图片正在采取期间时刻仍然构成了错的纵横比。长远的解决办法,这个--我想--将暂时用静止图像 (最后一个预览帧) 同时图片替换相机预览正在采取。我最终来试试这个。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: