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工具类

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @author pengbenlei
 * @Description
 * @create 2024-03-20 10:05
 */
@Slf4j
public class CommandUtil {

    /**
     * 执行 linux/cmd 命令
     *
     * @param command 命令
     * @return
     */
    public static boolean executeCommand(String command) {
        BufferedReader reader = null;
        InputStreamReader inputStreamReader = null;
        Process p;
        try {
            //判断是操作系统是linux还是windows
            String[] comds;
            if (System.getProperties().get("os.name").toString().toUpperCase().indexOf("WINDOWS") >= 0) {
                log.info("当前操作系统:windows");
                comds = new String[]{"cmd", "/c", command};
            } else {
                log.info("当前操作系统:linux");
                comds = new String[]{"/bin/sh", "-c", command};
            }

            // 开始执行命令
            log.info("执行命令:" + command);
            p = Runtime.getRuntime().exec(comds);
            //开启线程监听(此处解决 waitFor() 阻塞/锁死 问题)
            new RunThread(p.getInputStream(), "INFO").start();
            new RunThread(p.getErrorStream(), "ERROR").start();
            int flag = p.waitFor();

            return true;
        } catch (IOException e) {
            log.error("Proto2Html 执行命令IO异常!", e);
            return false;
        } catch (InterruptedException e) {
            log.error("Proto2Html 执行命令中断异常!", e);
            return false;
        } catch (Exception e) {
            log.error("Proto2Html 执行命令出现异常!", e);
            return false;
        } finally {
            IOUtils.closeQuietly(reader);
            IOUtils.closeQuietly(inputStreamReader);
        }
    }

    /**
     * 直接执行,获取执行内容返回
     *
     * @param command
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    public static String executeCommandGetReturn(String command) throws IOException, InterruptedException {
        System.out.println(command);
        Process process = Runtime.getRuntime().exec(command);
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = null;
        StringBuilder builder = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
        process.waitFor();
        int exitCode = process.exitValue();
        if (exitCode == 0) {
            return builder.toString();
        }
        return null;
    }
}

/**
 * 监听线程
 */
@Slf4j
class RunThread extends Thread {
    InputStream is;
    String printType;

    RunThread(InputStream is, String printType) {
        this.is = is;
        this.printType = printType;
    }

    public void run() {
        try {
            InputStreamReader isr = new InputStreamReader(is, "GBK");
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while ((line = br.readLine())!=null){
                log.info(printType + ">" + line);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}
命令执行工具类

 

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、违规审核需要结合人工,因为有些情况下会误判定,不能全靠自动识别。

posted @ 2024-03-18 10:58  Rolay  阅读(386)  评论(0编辑  收藏  举报