一、簡介
你是否還在為如何處理非法圖片而感到困惑?在涉及使用者檔案上傳的系統中,圖片內容的稽覈變得至關重要。不當圖片不僅影響使用者體驗,還可能帶來法律風險。依賴外部服務進行稽覈會帶來資料隱私問題和速度瓶頸。
爲了解決這些問題,我們引入了一個技術亮點:基於 TensorFlow 模型的本地圖片內容安全檢測,並整合在 SpringBoot 應用中
。這一方案可以在本地環境直接稽覈圖片內容,無需外部 API,確保資料私密性,同時提升稽覈速度和系統穩定性。透過這種本地化的智慧稽覈,系統能夠更自主高效地處理非法圖片,為使用者提供更安全的體驗。
二、NSFW 介紹
因為專案中會使用到 NSFW的模型,所以這裏簡單介紹一下
1. 描述
NSFW(Not Safe For Work)是一類用於檢測不適合在公共場所或工作環境中展示內容的模型。這些模型通常用於識別包含成人內容、暴力或其他不宜公開展示的圖片或影片。NSFW模型透過深度學習演算法進行訓練,能夠自動檢測這些不適合公開的內容,廣泛應用於社交媒體平臺、內容稽覈系統,以及其他需要過濾敏感內容的應用場景。
2. 評判指標
NSFW 模型透過以下幾個分類指標來判斷圖片屬於哪一類內容。使用者可以設定對應的機率閾值來判定圖片是否符合某一類:
DRAWINGS: 卡通或漫畫圖片,這類圖片通常為手繪或數字繪圖風格。
HENTAI: 帶有情色成分的動畫或漫畫,包含成人內容但以動畫風格呈現。
NEUTRAL: 正常的、適合公開展示的影象,無不當內容。
PORN: 色情圖片,包含明顯的成人內容和性行為。
SEXY: 暗示性強的影象,雖然不完全屬於色情類別,但具有強烈的性暗示。
這些分類指標幫助系統自動化地對影象內容進行分類和過濾,從而有效地防止不適當內容的傳播。
三、功能演示
當我們上傳一張正常的圖片
當我們上傳一張具有違規行為的圖片
四、編碼實現
1. 引入依賴
<dependency> <groupId>org.tensorflow</groupId> <artifactId>tensorflow</artifactId> </dependency>
2. 初始化NSFW模型
NSFW 模型可以透過 GitHub 獲取,推薦使用 nsfwjs 專案中的模型。可以使用 Python 將模型轉換為 TensorFlow 的
saved_model
格式,然後在 SpringBoot 應用中進行載入。當然,也可以直接找到已轉換好的 NSFWsaved_model
格式的模型進行載入。
注意事項:
在打包後
new ClassPathResource("").getFile().getAbsolutePath();
的訪問會出現問題(文章末尾描述解決辦法)
/** * NSFW 模型 * * @author : YiFei */ @Getter @Component public class NSFWModelService { // 提供方法來獲取 TensorFlow Session private Session session; @PostConstruct public void init() { // 載入 TensorFlow 模型 try { String modelAbsolutePath = new ClassPathResource("tensorflow/saved_model/nsfw").getFile().getAbsolutePath(); SavedModelBundle model = SavedModelBundle.load(modelPath, "serve"); this.session = model.session(); } catch (IOException e) { throw new RuntimeException(e); } } /** * 在銷燬 Bean 時關閉 TensorFlow Session */ @PreDestroy public void closeSession() { this.session.close(); } }
如果需要輸出模型的詳細資訊,可以加上以下程式碼在 SavedModelBundle model = SavedModelBundle.load(modelPath, "serve");
後
// 以下是獲取模型 Inputs 資料格式 、輸入張量名 , output 資料格式 、輸出張量名 // MetaGraphDef metaGraphDef = MetaGraphDef.parseFrom(model.metaGraphDef()); // Map<String, SignatureDef> signatureDefMap = metaGraphDef.getSignatureDefMap(); // // for (Map.Entry<String, SignatureDef> entry : signatureDefMap.entrySet()) { // System.out.println("SignatureDef key: " + entry.getKey()); // // SignatureDef signatureDef = entry.getValue(); // System.out.println("Inputs:"); // for (Map.Entry<String, TensorInfo> inputEntry : signatureDef.getInputsMap().entrySet()) { // String inputKey = inputEntry.getKey(); // TensorInfo inputTensorInfo = inputEntry.getValue(); // // // 列印輸入張量的名稱 // System.out.println(" Key: " + inputKey); // System.out.println(" Name: " + inputTensorInfo.getName()); // // // 列印輸入張量的形狀 // if (inputTensorInfo.hasTensorShape()) { // TensorShapeProto tensorShape = inputTensorInfo.getTensorShape(); // System.out.println(" Shape: " + tensorShape); // } // // // 列印輸入張量的資料型別 // System.out.println(" Data Type: " + inputTensorInfo.getDtype()); // } // // System.out.println("Outputs:"); // for (Map.Entry<String, TensorInfo> outputEntry : signatureDef.getOutputsMap().entrySet()) { // String outputKey = outputEntry.getKey(); // TensorInfo outputTensorInfo = outputEntry.getValue(); // // // 列印輸出張量的名稱 // System.out.println(" Key: " + outputKey); // System.out.println(" Name: " + outputTensorInfo.getName()); // // // 列印輸出張量的形狀 // if (outputTensorInfo.hasTensorShape()) { // TensorShapeProto tensorShape = outputTensorInfo.getTensorShape(); // System.out.println(" Shape: " + tensorShape.toString()); // } // // // 列印輸出張量的資料型別 // System.out.println(" Data Type: " + outputTensorInfo.getDtype()); // } // }
3. 編寫工具類
儘管 TensorFlow API 已經相對簡潔,但在實際使用中仍可能顯得繁瑣。爲了解決這一問題,我們可以封裝一個工具類,使介面更加友好,讓開發者只需一行程式碼即可完成圖片內容安全的校驗,而無需編寫大量冗餘的程式碼。透過這個工具類,您可以更輕鬆地整合 NSFW 模型,並提高專案的開發效率。
工具類的具體實現讀者無需深入理解,只需瞭解其輸入輸出資訊以及如何使用即可。由於在之前的文章中工具類程式碼過多,影響了閱讀體驗,因此在這裏我貼出了使用到的工具類原始碼以及大致介紹。
NSFWAnalyzerUtils.java
Map<String, String> getNsfwPredictions(MultipartFile file)
:該方法接收一個MultipartFile
物件,先透過Image.read()
將圖片轉換為BufferedImage
,然後提取圖片的 RGB 值作為模型輸入張量,最後解析模型輸出張量並返回各個 NSFW 指標的機率。boolean isNsfwFile(MultipartFile file)
:該方法與getNsfwPredictions(MultipartFile file)
的處理過程類似,但增加了開發者設定的閾值判斷。當檢測到的非法指標機率達到或超過該閾值時,圖片將被判定為非法。TensorflowUtil.java
static String getModelPath(String classPathResource)
將Resource下的save_model檔案轉換到臨時檔案中,返回臨時檔案絕對路徑。
4. 編寫 RESTful 介面
@RestController @RequestMapping("nsfw") @RequiredArgsConstructor public class NsfwController { private final NSFWAnalyzerUtils nsfwAnalyzerUtils; @Operation(summary = "圖片檢測") @PreventDuplicateSubmit @PostMapping("/check") public Result<Map<String, String>> nsfwCheck(MultipartFile file) { try { return Result.success(nsfwAnalyzerUtils.getNsfwPredictions(file)); } catch (Exception e) { throw new ServiceException(ResultCode.FILE_ANALYZER_ERROR); } } }
五、解決模型載入時部署問題
在專案被打成 Jar 包後,使用 new ClassPathResource("XXX").getFile().getAbsolutePath();
是無法訪問到資源的絕對路徑的。爲了解決這個問題,我們透過將 resource 中 save_model
目錄下的所有檔案轉存到臨時目錄,再將臨時目錄的路徑返回給模型進行載入。