opencv的sdk版本3.4.7,模块名openCVLibrary347。
如果分析一个视频的每秒的图片,对于那么大量的图片,但是只想找到有人脸的图片,并保存在另一个文件夹,该怎么做。
这里,想到了用一个比较简便的方法,级联分类器去作为人脸对比,只要有xml文件就可以,比较方便。
这里的两个xml文件,放在main下方的新建的一个assets里面,这样打包成apk方便读取。
assets读取路径的代码,用的是下面的代码,是将原来的改造了一下,改为一个Context context变量,去读取活动的上下文。
因为在安卓里面,面向的一个操作对象都是一个活动一个活动的,一些函数或者方法没有Context就用不了。
private String copyAssetGetFilePath(String fileName,Context context) {
try {
File cacheDir = context.getCacheDir();
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
File outFile = new File(cacheDir, fileName);
if (!outFile.exists()) {
boolean res = outFile.createNewFile();
if (!res) {
return null;
}
} else {
if (outFile.length() > 10) {//表示已经写入一次
return outFile.getPath();
}
}
InputStream is = context.getAssets().open(fileName);
FileOutputStream fos = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int byteCount;
while ((byteCount = is.read(buffer)) != -1) {
fos.write(buffer, 0, byteCount);
}
fos.flush();
is.close();
fos.close();
return outFile.getPath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
上面的级联列表看到了,是有两个的,分别对应的是动漫的人脸,还有一个是电影现实中的人脸。
所以对应的就有两个方法,分别对这两种人脸做对比的。
public void comic_imgurl_detect(String[] imgurl,Context context,String dirpath){
CascadeClassifier comic=get_comic_jilian( context);
per_imgurl(imgurl, dirpath, comic);
}
public void movie_imgurl_detect(String[] imgurl,Context context,String dirpath){
CascadeClassifier comic=get_movie_jilian( context);
per_imgurl(imgurl, dirpath, comic);
}
参数String[] imgurl,是一个字符串的数组,里面是一个图片文件夹里面的所有图片的它们的路径,组成的一个字符串的数组。
参数Context context,活动的上下文,比如说你设置了一个button,并且设置了点击事件,context就是v.getContext(),就像下面这样。
public void onClick(View v) {
v.getContext()
}
参数String dirpath,源文件夹的路径,作用是用来拼接人脸图片所在文件夹及文件图片的。
String dirpathaa = dirpath+"/人脸";
File dirpathaa2 = new File(dirpathaa);
if (!dirpathaa2.exists()) {
dirpathaa2.mkdirs();
}
比如另存为文件夹人脸,就要新建这个文件夹,方便保存之后识别出来的含人脸的图片。
获得一个级联分类器的方法如下。
public CascadeClassifier get_movie_jilian(Context context){
CascadeClassifier comic_face=new CascadeClassifier();
String path1=copyAssetGetFilePath("lbpcascade_frontalface_improved.xml",context);
comic_face.load(path1);
return comic_face;
}
上面是得到电影人脸的,如果是动漫人脸的话,就把xml文件名改为lbpcascade_animeface.xml就可以了。
之后还有一个另外的函数方法,就是,路径剪切出图片文件名,也是用来作为保存人脸图片路径用的。
public static String id_to_picname(String descPath){
String[] split = descPath.split("/");
String fileName = split[split.length - 1];
return fileName;
}
就是截取最后一个,就是这个图片名。
判断一张图片,是否含人脸的函数如下。
public boolean detect_face(CascadeClassifier jilian,Mat img1){
MatOfRect faces=new MatOfRect();
Mat mat11 = new Mat();
Imgproc.cvtColor(img1, mat11, Imgproc.COLOR_RGB2GRAY);
jilian.detectMultiScale(mat11,faces,1.1);
List<Rect> facelist=faces.toList();
if(facelist.size()>0){
mat11.release();
return true;
}else {
return false;
}
}
输入一个级联分类器和图片Mat作为参数,如果有人脸,那么facelist的长度是大于0的,然后返回true就可以了。
如果有人脸,想要保存,就用imwrite进行操作。
String pic_name=id_to_picname(pathaa);
String outfileimg=dirpathaa+"/"+pic_name;
imwrite(outfileimg, img1);
img1.release();
最后给出总的代码如下:
package com.example.agrdf.picturecompare;
import android.content.Context;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import static org.opencv.imgcodecs.Imgcodecs.imwrite;
/**
* Created by agrdf on 2022/12/14.
*/
public class jilian {
public void comic_imgurl_detect(String[] imgurl,Context context,String dirpath){
CascadeClassifier comic=get_comic_jilian( context);
per_imgurl(imgurl, dirpath, comic);
}
public void movie_imgurl_detect(String[] imgurl,Context context,String dirpath){
CascadeClassifier comic=get_movie_jilian( context);
per_imgurl(imgurl, dirpath, comic);
}
public Mat cv_read(String res){
Mat img = Imgcodecs.imread(res, Imgcodecs.IMREAD_UNCHANGED);
return img;
}
public CascadeClassifier get_comic_jilian(Context context){
CascadeClassifier comic_face=new CascadeClassifier();
String path1=copyAssetGetFilePath("lbpcascade_animeface.xml",context);
comic_face.load(path1);
return comic_face;
}
public CascadeClassifier get_movie_jilian(Context context){
CascadeClassifier comic_face=new CascadeClassifier();
String path1=copyAssetGetFilePath("lbpcascade_frontalface_improved.xml",context);
comic_face.load(path1);
return comic_face;
}
public boolean detect_face(CascadeClassifier jilian,Mat img1){
MatOfRect faces=new MatOfRect();
Mat mat11 = new Mat();
Imgproc.cvtColor(img1, mat11, Imgproc.COLOR_RGB2GRAY);
jilian.detectMultiScale(mat11,faces,1.1);
List<Rect> facelist=faces.toList();
if(facelist.size()>0){
mat11.release();
return true;
}else {
return false;
}
}
public void per_imgurl(String[] imgurl,String dirpath,CascadeClassifier comic){
String dirpathaa = dirpath+"/人脸";
File dirpathaa2 = new File(dirpathaa);
if (!dirpathaa2.exists()) {
dirpathaa2.mkdirs();
}
for (int b = 0; b < imgurl.length; b++) {
String pathaa=imgurl[b];
Mat img1=cv_read(pathaa);
if(detect_face( comic, img1)){
String pic_name=id_to_picname(pathaa);
String outfileimg=dirpathaa+"/"+pic_name;
imwrite(outfileimg, img1);
img1.release();
}
}
}
private String copyAssetGetFilePath(String fileName,Context context) {
try {
File cacheDir = context.getCacheDir();
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
File outFile = new File(cacheDir, fileName);
if (!outFile.exists()) {
boolean res = outFile.createNewFile();
if (!res) {
return null;
}
} else {
if (outFile.length() > 10) {//表示已经写入一次
return outFile.getPath();
}
}
InputStream is = context.getAssets().open(fileName);
FileOutputStream fos = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int byteCount;
while ((byteCount = is.read(buffer)) != -1) {
fos.write(buffer, 0, byteCount);
}
fos.flush();
is.close();
fos.close();
return outFile.getPath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String id_to_picname(String descPath){
String[] split = descPath.split("/");
String fileName = split[split.length - 1];
return fileName;
}
}