先看效果
1.下载OpenCV
官网地址:opcv官网
找到Android 4.10.0版本下载
下载完毕 解压zip如图:
2.将OpenCV-android_sdk导入项目 我这里用的最新版的Android studio
如果是java开发 需要添加kotlin的支持。我用的studio比较新可以参考下,如果studio版本比较老 请在网上自行寻找 kotlin支持
引入完成后如下
3.编写自定义javaCameraView
public class OpenCVCameraView extends JavaCameraView implements CameraBridgeViewBase.CvCameraViewListener2 {
private boolean isDetecting = true;
private CascadeClassifier faceDetector;
private Mat mRgba;
private Mat mGray;
private boolean isSaving = false;
private File cascadeFile;
private DetectionCallback detectionCallback;
private int[] previewSize = new int[2];
private Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255); // 人脸框颜色(绿色)
private Scalar ROI_COLOR = new Scalar(255, 0, 0, 255); // 识别区域颜色(蓝色)
private int frameWidth, frameHeight;
private int centerX, centerY, radius;
public interface DetectionCallback {
void onFaceDetected(Bitmap bitmap);
void onDetectionStopped();
}
private Bitmap bitmap = null;
private long lastDetectionTime = 0;
public OpenCVCameraView(Context context, AttributeSet attrs) {
super(context, attrs);
initFaceDetection();
}
public void setDetectionCallback(DetectionCallback callback) {
this.detectionCallback = callback;
}
public void startDetection() {
isDetecting = true;
lastDetectionTime = System.currentTimeMillis();
}
public void stopDetection() {
isDetecting = false;
if (detectionCallback != null) {
detectionCallback.onDetectionStopped();
}
}
private void initFaceDetection() {
try {
// 加载人脸检测分类器
InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
File cascadeDir = getContext().getDir("cascade", Context.MODE_PRIVATE);
cascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
FileOutputStream os = new FileOutputStream(cascadeFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
is.close();
os.close();
faceDetector = new CascadeClassifier(cascadeFile.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onCameraViewStarted(int width, int height) {
mRgba = new Mat();
mGray = new Mat();
}
@Override
public void onCameraViewStopped() {
mRgba.release();
mGray.release();
}
@Override
protected boolean initializeCamera(int width, int height) {
return super.initializeCamera(width, height);
}
@Override
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba(); // 使用灰度图进行检测
frameWidth = mRgba.cols();
frameHeight = mRgba.rows();
centerX = frameWidth / 2;
centerY = frameHeight / 2;
radius = Math.min(frameWidth, frameHeight) / 4; //调整半径
Core.flip(mRgba, mRgba, 1); // 参数 1 表示水平翻转
// 检测人脸
MatOfRect faces = new MatOfRect();
if (faceDetector != null) {
faceDetector.detectMultiScale(mRgba, faces, 1.1, 3, 0,
new Size(30, 30), new Size(200, 200));
}
// 绘制中间圆形识别区域
// Imgproc.circle(mRgba, new Point(centerX, centerY), radius, ROI_COLOR, 2);
long currentTime = System.currentTimeMillis();
// 过滤并绘制符合条件的人脸
if (isDetecting && (currentTime - lastDetectionTime > 500)) {
Rect[] facesArray = faces.toArray();
for (Rect face : facesArray) {
int faceCenterX = face.x + face.width / 2;
int faceCenterY = face.y + face.height / 2;
double distance = Math.sqrt(Math.pow(faceCenterX - centerX, 2) + Math.pow(faceCenterY - centerY, 2));
// 仅处理位于圆形区域内的人脸
if (distance <= radius) {
saveImageWithFace(mRgba,face);
Imgproc.rectangle(mRgba,
new Point(face.x, face.y),
new Point(face.x + face.width, face.y + face.height),
FACE_RECT_COLOR, 2);
if (detectionCallback != null && bitmap != null) {
detectionCallback.onFaceDetected(bitmap);
}
}
}
lastDetectionTime = currentTime;
}
return mRgba; // 返回处理后的帧
}
@SuppressLint("StaticFieldLeak")
private void saveImageWithFace(Mat rgbaMat, Rect faceRect) {
isSaving = true;
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
try {
// 裁剪人脸区域
Mat faceMat = new Mat(rgbaMat, faceRect);
// 转换为Bitmap
bitmap = Bitmap.createBitmap(
faceMat.cols(),
faceMat.rows(),
Bitmap.Config.ARGB_8888
);
Utils.matToBitmap(faceMat, bitmap);
// 保存到相册
//saveToGallery(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
isSaving = false;
}
}.execute();
}
public void restartDetection() {
isDetecting = true;
Log.d("restartDetection", "restartDetection: 重新开始检测");
}
private void saveToGallery(Bitmap bitmap) {
String fileName = "Face_" + System.currentTimeMillis() + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
ContentResolver resolver = getContext().getContentResolver();
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream out = resolver.openOutputStream(uri)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
Log.d("图片保存", "saveToGallery: " + fileName);
// Toast.makeText(getContext(), "保存成功: " + fileName, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
Toast.makeText(getContext(), "保存失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}
openCVCameraView 包含的功能:500 毫秒检测一次人脸通过lastDatectionTIme判断,打开检测,和关闭检测 startDetection,stopDetection,还可以识别人脸后保存到相册saveToGallery,只检测区域的人脸 防止误检测
4.在layout 里使用 再初始化
mOpenCvCameraView = findViewById(R.id.camera_view);
mOpenCvCameraView.setCvCameraViewListener(mOpenCvCameraView);
mOpenCvCameraView.setDetectionCallback(this);
@Override
protected void onResume() {
super.onResume();
if (OpenCVLoader.initDebug()) {
//0:后置摄像头 1:前置摄像头
mOpenCvCameraView.setCameraIndex(0);
mOpenCvCameraView.enableView();
mOpenCvCameraView.setCameraPermissionGranted();
}
}
@Override
protected void onPause() {
super.onPause();
if (mOpenCvCameraView != null) {
mOpenCvCameraView.disableView();
}
}