使用雅虎开源的 TensorFlow 2 Open-NSFW 模型,NSFW:not safe for work,工作场所不宜
Python 3.7 及以上,安装 opennsfw2 库。图片素材请参考 小结中地址进行下载。
pip install opennsfw2
图片识别 代码如下:
import opennsfw2 as n2
# 将自动下载预训练模型 open_nsfw_weights.h5 到 C:\Users\Administrator\.opennsfw2\weights
# pip install opennsfw2
# 单张预测
image_path = '1.jpg'
nsfw_probability = n2.predict_image(image_path)
print(nsfw_probability)
# 0.16282974183559418
# 批量预测
image_paths = ['1.jpg', '2.jpg']
nsfw_probabilities = n2.predict_images(image_paths)
print(nsfw_probabilities)
# [0.16282965242862701, 0.8638442158699036]
视频识别 代码如下:
import opennsfw2 as n2
video_path = '1.mp4'
elapsed_seconds, nsfw_probabilities = n2.predict_video_frames(video_path)
for second, probability in zip(elapsed_seconds, nsfw_probabilities):
print(f'{second:.2f}s: {probability * 100:.0f} %')
# 0.03s: 1%
# ...
# 10.01s: 87.00%
# ...
# 10.64s: 69.00%
import numpy as np
from PIL import Image
from opennsfw2._model import make_open_nsfw_model
from opennsfw2._image import preprocess_image, Preprocessing
image_path = '1.jpg'
image = preprocess_image(Image.open(image_path), Preprocessing.YAHOO)
model = make_open_nsfw_model()
nsfw_probability = float(model.predict(np.expand_dims(image, 0), batch_size=1)[0][1])
print(nsfw_probability)
# 0.16282974183559418
import time
import numpy as np
import tkinter as tk
from pathlib import Path
from tkinter import filedialog
from tkinter import messagebox
from PIL import ImageTk, Image
from opennsfw2._model import make_open_nsfw_model
from opennsfw2._image import preprocess_image, Preprocessing
begin = time.time()
model = make_open_nsfw_model() # 加载模型
elapsed = time.time() - begin # 加载模型耗时
initialdir = Path.cwd() # 初始化目录,可切换为图片Path.home() / 'Pictures'
img = None # 当前打开的图片
def scale(size, width=None, height=None):
"""获取按比例缩放后的宽高"""
if not width and not height:
width, height = size
if not width or not height:
_width, _height = size
height = width * _height / _width if width else height
width = height * _width / _height if height else width
return int(width), int(height)
def img_resize(event=None):
"""显示图片"""
global img
if img:
_img = img.resize(scale(img.size, height=win.winfo_height()))
_img = ImageTk.PhotoImage(_img)
label.config(image=_img)
label.image = _img
def on_closing():
"""关闭事件"""
if messagebox.askokcancel('关闭', '是否退出程序?'):
win.destroy()
def open_file(event=None):
"""打开图片"""
global initialdir
global img
file_path = filedialog.askopenfilename(title='选择图片', initialdir=initialdir,
filetypes=[('image files', ('.png', '.jpg', '.jpeg', '.gif'))])
if file_path:
statusbar.config(text='正在加载...')
statusbar.update_idletasks()
begin = time.time()
path = Path(file_path)
initialdir = path.parent
img = Image.open(file_path)
img_resize()
_img = preprocess_image(Image.open(file_path), Preprocessing.YAHOO)
probability = float(model.predict(np.expand_dims(_img, 0), batch_size=1)[0][1])
print(probability)
end = time.time()
statusbar.config(text=f'{path.name} 耗时: {end - begin:.2f}s 概率: {probability * 100:.2f} %')
win = tk.Tk()
win.title('黄图检测') # 标题
menu = tk.Menu(win)
menu.add_command(label='打开', command=open_file)
win.config(menu=menu)
win.bind('<Configure>', img_resize)
win.geometry('600x300+300+300')
win.minsize(200, 200)
win.protocol('WM_DELETE_WINDOW', on_closing)
statusbar = tk.Label(win, text=f'加载模型耗时: {elapsed:.2f}s', bd=1, relief=tk.SUNKEN, anchor=tk.W, name='statusbar')
statusbar.pack(side=tk.BOTTOM, fill=tk.X)
label = tk.Label(win, text='双击打开图片')
label.bind('<Double-Button-1>', open_file)
label.pack(fill=tk.BOTH, expand=True)
win.mainloop()
import time
import threading
import tkinter as tk
from pathlib import Path
from tkinter import filedialog
from tkinter import messagebox
from timeit import default_timer as timer
import imageio
import numpy as np
from PIL import ImageTk, Image
from opennsfw2._model import make_open_nsfw_model
from opennsfw2._image import preprocess_image, Preprocessing
# pip install imageio-ffmpeg
begin = time.time()
model = make_open_nsfw_model() # 加载模型
elapsed = time.time() - begin # 加载模型耗时
initialdir = Path.cwd() # 初始化目录,可切换为图片Path.home() / 'Pictures'
reader = None # 视频读取器
accum_time = 0
curr_fps = 0
last_fps = 0
prev_time = timer()
def on_closing():
"""关闭事件"""
if messagebox.askokcancel('关闭', '是否退出程序?'):
win.destroy()
def play():
global reader
global prev_time, accum_time, curr_fps
for image in reader:
image = Image.fromarray(image)
frame_image = ImageTk.PhotoImage(image)
label.config(image=frame_image)
label.image = frame_image
_img = preprocess_image(image, Preprocessing.YAHOO)
probability = float(model.predict(np.expand_dims(_img, 0), batch_size=1)[0][1])
# FPS
curr_time = timer()
exec_time = curr_time - prev_time
prev_time = curr_time
accum_time = accum_time + exec_time
curr_fps = curr_fps + 1
if accum_time > 1:
accum_time = accum_time - 1
last_fps = curr_fps
curr_fps = 0
statusbar.config(text=f'概率: {probability * 100:.2f} % FPS: {last_fps}')
def open_file(event=None):
"""打开视频"""
global initialdir
global reader
file_path = filedialog.askopenfilename(title='选择视频', initialdir=initialdir,
filetypes=[('Select files', ('.mp4', '.mkv', '.avi', '.wmv'))])
if file_path:
statusbar.config(text='正在加载...')
statusbar.update_idletasks()
path = Path(file_path)
initialdir = path.parent
reader = imageio.get_reader(path)
thread = threading.Thread(target=play, daemon=True)
thread.start()
win = tk.Tk()
win.title('黄图检测') # 标题
menu = tk.Menu(win)
menu.add_command(label='打开', command=open_file)
win.config(menu=menu)
win.geometry('1280x720+300+300')
win.minsize(200, 200)
win.protocol('WM_DELETE_WINDOW', on_closing)
statusbar = tk.Label(win, text=f'加载模型耗时: {elapsed:.2f}s', bd=1, relief=tk.SUNKEN, anchor=tk.W, name='statusbar')
statusbar.pack(side=tk.BOTTOM, fill=tk.X)
label = tk.Label(win, text='双击打开视频')
label.bind('<Double-Button-1>', open_file)
label.pack(fill=tk.BOTH, expand=True)
win.mainloop()
import io
import pyglet
import numpy as np
from PIL import Image
from opennsfw2._model import make_open_nsfw_model
from opennsfw2._image import preprocess_image, Preprocessing
# pip install pyglet
model = make_open_nsfw_model()
filename = '1.mp4'
source = pyglet.media.load(filename)
video_format = source.video_format
width, height = video_format.width, video_format.height
title = 'Video Player'
window = pyglet.window.Window(width, height, title)
player = pyglet.media.Player()
player.queue(source)
player.play()
@window.event
def on_draw():
window.clear()
if player.source and player.source.video_format:
player.get_texture().blit(0, 0, width=width, height=height)
image_data = player.get_texture().get_image_data()
pitch = -(image_data.width * len('RGB'))
data = image_data.get_data('RGB', pitch)
_img = preprocess_image(Image.frombytes('RGB', (width, height), data, 'raw'), Preprocessing.YAHOO)
probability = float(model.predict(np.expand_dims(_img, 0), batch_size=1)[0][1])
print(probability)
pyglet.app.run()
运行结果:
效率分析:
get_data() 0.941 s
frombytes() 0.001 s
preprocess_image() 0.006 s
predict() 0.052 s
参考: https://blog.csdn.net/lly1122334/article/details/121247781
https://github.com/bhky/opennsfw2
资源: https://img-blog.csdnimg.cn/20210702231858370.jpg
http://www.lenna.org/full/len_full.jpg