Python:使用opennsfw2对图片/视频进行鉴黄识别


简介

使用雅虎开源的 TensorFlow 2 Open-NSFW 模型,NSFW:not safe for work,工作场所不宜

实践

1.环境准备,

Python 3.7 及以上,安装 opennsfw2 库。图片素材请参考 小结中地址进行下载。

pip install opennsfw2

2.代码实践

图片识别 代码如下:

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%

高级用法

1. 加载的方式

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

2.车速检测

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()

3. 视频车速检测(无声)

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()

4. 视频车速检测(有声)

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

猜你喜欢

转载自blog.csdn.net/zhanggqianglovec/article/details/128188702