java 违规图片、视频自动识别(色情、血腥、暴力)
最近工作上有需要,需要对用户上传的图片和视频做违规自动识别,网上当然有各种大厂的接口可以调用,但是由于项目的特殊性,不能使用外网,所以只有自己弄了。
查询资料,网上都是各种python的文章,也测试过一些,由于我对python不是很熟悉,各种环境依赖没整明白,考虑到后期还要上线到服务器,如果环境这么复杂,可能对现场实施人员不太友好。
后续找到有spring的包:open-nsfw-spring-boot-starter,提供了违规图片的识别。
我的方案目前是:
1、图片违规识别直接使用三方工具。
2、视频违规识别,首先按帧截图,保存为图片,再使用图片的识别方式。
这种方案缺点就是使用过程中对文件IO开销比较大,但是目前也找不到其他方案了,下面上代码。
1、maven引用工具包
<dependency>
<groupId>com.ruibty.nsfw</groupId>
<artifactId>open-nsfw-spring-boot-starter</artifactId>
<version>1.0</version>
</dependency>
2、视频文件抽帧处理,我这里用的是ffmpeg,可自行替换。我这里使用的是直接执行commond命令,因为ffmpeg不支持多线程,所以这里做了限制。
/**
* 视频文件帧处理
*
* @param filePath 帧文件保存地址
* @return
*/
public synchronized static String videoFrame(String filePath) {
boolean exists = Files.exists(Paths.get(filePath));
Assert.isTrue(exists, "原文件不存在!");
Path path = Paths.get(filePath);
// 文件名
String fileName = path.getFileName().toString();
int dotIndex = fileName.lastIndexOf('.');
String fileNameWithoutExtension = dotIndex == -1 ? fileName : fileName.substring(0, dotIndex);
Path parentPath = path.getParent();
// 父级文件夹
String parentDirPath = parentPath == null ? "" : parentPath.toString();
// 使用文件名创建一个文件夹,准备存帧文件
String frameFilePath = parentDirPath + "/" + fileNameWithoutExtension + "_" + "video_frame";
File tsDir = new File(frameFilePath);
if (!tsDir.exists()) {
Assert.isTrue(tsDir.mkdirs(), "生成帧文件目录出错,生成目录:" + frameFilePath);
}
StringBuilder command = new StringBuilder();
command.append(LeenledaConfig.getFfmpegPath());
command.append(" ffmpeg ");
command.append(" -i ");
command.append(filePath);
command.append(" -vf \"fps=1/3\" -qscale:v 2 ");
command.append(frameFilePath);
command.append("/video_frm_%05d.jpg");
try {
if (!executeCommandBatch(command.toString())) {
return null;
}
} catch (Exception ex) {
ex.printStackTrace();
}
return frameFilePath;
}
3、执行cmmond命令方法
/**
* 批量命令执行,等待返回结果
*
* @param cmd
* @return
* @throws IOException
* @throws InterruptedException
*/
private static boolean executeCommandBatch(String cmd) throws IOException, InterruptedException {
System.out.println(cmd);
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String readLine = br.readLine();
StringBuilder builder = new StringBuilder();
while (readLine != null) {
readLine = br.readLine();
builder.append(readLine + "\n");
}
p.waitFor();
int i = p.exitValue();
if (i == 0) {
System.out.println(builder);
return true;
} else {
return false;
}
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (br != null) {
br.close();
}
}
}
3、违规图片识别方法
/**
* 图片违规处理 仅支持单线程调用
*
* @param filePath 文件目录
*/
public synchronized void violationIdentificationByDyFile(Long fileId, String filePath) {
boolean flagViolation = false;
Path path = Paths.get(filePath);
byte[] imageBytes = null;
try {
imageBytes = Files.readAllBytes(path);
} catch (IOException e) {
e.printStackTrace();
}
float prediction = nsfwService.getPrediction(imageBytes);
if (prediction >= MAXPREDICTION) {
flagViolation = true;
}
if (flagViolation) {
// 违规文件
} else {
// 文件未违规
}
}
4、MAXPREDICTION是我定义的一个静态变量,prediction 是系数,一般来说,0.8以上的文件都属于违规了。
5、违规审核需要结合人工,因为有些情况下会误判定,不能全靠自动识别。