最近工作上有需要,需要對用戶上傳的圖片和視頻做違規自動識別,網上當然有各種大廠的接口可以調用,但是由於項目的特殊性,不能使用外網,所以只有自己弄了。
查詢資料,網上都是各種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、違規審覈需要結合人工,因爲有些情況下會誤判定,不能全靠自動識別。