目录
前言
本项目通过爬虫技术获取图片,利用OpenCV库对图像进行处理,识别并切割出人物脸部,形成了一个用于训练的数据集。通过ImageAI进行训练,最终实现了对动漫人物的识别模型。同时,本项目还开发了一个线上Web应用,使得用户可以方便地体验和使用该模型。
首先,项目使用爬虫技术从网络上获取图片。这些图片包含各种动漫人物,其中我们只对人物脸部进行训练,所以我们会对图像进行处理,并最终将这些图像将作为训练数据的来源。
其次,利用OpenCV库对这些图像进行处理,包括人脸检测、图像增强等步骤,以便准确识别并切割出人物脸部。这一步是为了构建一个清晰而准确的数据集,用于模型的训练。
接下来,通过ImageAI进行训练。ImageAI是一个简化图像识别任务的库,它可以方便地用于训练模型,这里用于训练动漫人物的识别模型。
最终,通过项目开发的线上Web应用,用户可以上传动漫图像,系统将使用训练好的模型识别图像中的动漫人物,并返回相应的结果。
总的来说,本项目结合了爬虫、图像处理、深度学习和Web开发技术,旨在提供一个便捷的动漫人物识别服务。这对于动漫爱好者、社交媒体平台等有着广泛的应用前景。
总体设计
本部分包括系统整体结构图和系统流程图。
系统整体结构图
系统整体结构如图所示。
系统流程图
系统流程如图所示。
运行环境
本部分包括爬虫、模型训练及实际应用运行环境。
爬虫
安装Python3.6以上及Selenium3.0.2版本。
详见博客。
模型训练
本部分包括安装依赖、安装ImageAI。
详见博客。
实际应用
实际应用包括前端开发环境和后端环境的搭建。
详见博客。
模块实现
本项目包括4个模块:数据准备、数据处理、模型训练及保存、模型测试,下面分别介绍各模块的功能及相关代码。
1. 数据准备
本项目的数据来自于百度图片,通过爬虫获取。
1)爬虫下载原始图片
详见博客。
2)手动筛选图片
部分人物的名称、现实事物或人物有重名现象,加上一些图片质量不佳,需要人为剔除,手动筛选。
详见博客。
2. 数据处理
将图片中的人脸裁剪进行模型训练,切割人脸部分由OpenCV通过训练好的动漫人物脸部识别模型lbpcascade_animeface
截取人物脸部。GitHub下载地址为https://github.com/nagadomi/lbpcascade_animeface。
1)切割得到人物脸部
相关代码如下:
#基本参数设定
SRC = "Raw" #待处理的文件路径
DST = "Data" #处理后的文件路径
TRAIN_PER = 5 #训练的图片比例
TEST_PER = 1 #测试的图片比例
#处理原图片得到人物脸部图片并按比例分配训练和测试用于训练模型
for image_file in files: #读取所有图片
image_file = image_file.replace('\\', '/') #解决Windows下的文件路径问题
target_path = "/".join(image_file.strip("/").split('/')[1:-1])
target_path = os.path.join(dst, target_path) + "/"
if not os.path.exists(target_path):
os.makedirs(target_path)
count = len(os.listdir(target_path)) + 1
image = cv2.imdecode(np.fromfile(image_file, dtype=np.uint8), -1)
#解决中文路径读入图片问题
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #OpenCV的设置
gray = cv2.equalizeHist(gray) #转化为灰度图片
faces = cascade.detectMultiScale(gray,
scaleFactor=1.05, #指定每个图像缩放比例,缩小图像大小的参数
minNeighbors=4, #此参数将影响检测到的面孔,值越高,检测结果越少,质量越好
minSize=(24, 24) #最小对象大小或者小于此值的对象将被忽略
for (x, y, w, h) in faces:
crop_img = image[y:y + h, x:x + w]
crop_img = cv2.resize(crop_img, (96, 96)) #重置为96*96
filename = os.path.basename(image_file).split('.')[0] cv2.imencode('.jpg',crop_img)[1].tofile(os.path.join(target_path, str(count) + ".jpg")) #保存切割的脸部
处理前和处理后的效果如图所示。
2)重新命名处理后的图片
对于处理后的图片,需要重新指定文件名称以便于统计和处理。相关代码如下:
def rename_files(dir, prefix='', joiner='_', startNum=0,
changeType='', ignoreType='', typeOnly=''):
'''重命名一个文件夹中的所有文件
Args:
dir(string): 重命名文件夹的路径
prefix(string): 文件名前缀
joiner(string): 连接文件名前缀和数字的连接符,默认为下划线
startNum(int): 重命名文件的开始数字,默认为0
changeType(string): 把文件重命名为指定类型,默认不指定类型
typeOnly(string): 只处理指定类型的文件,使用空格分割,例如“.jpg .jpeg .png .bmp .webp”
ignoreType(string): 忽略处理文件的类型,使用空格分割,例如,“.py .docx”
'''
for root, _, files in os.walk(dir):
root = root.replace('\\', '/')
if prefix == '':
prefix = root.split('/')[-1]
count = startNum
for file in files:
true_type = os.path.splitext(file)[-1] #文件真实类型
type_list = typeOnly.split()
ignore_list = ignoreType.split()
if true_type in type_list or len(type_list) == 0:
if true_type in ignore_list:
continue
if changeType == '': #是否指定改变类型
file_type = true_type
else:
file_type = changeType
new_name = "{}{}{}{}".format(
prefix, joiner, str(count), file_type)
path = os.path.join(root, new_name)
old_path = os.path.join(root, file)
if old_path == path:
continue
if not os.path.exists(path):
os.rename(old_path, path)
count = count + 1
def main():
parser = argparse.ArgumentParser(description="重命名指定文件夹下的所有文件")
parser.add_argument("dir", type=str, help="重命名文件的路径")
parser.add_argument("--prefix", "-p", type=str,
default='', help="前缀,默认为文件名")
parser.add_argument("--joiner", "-j", type=str, default="_", help="连接符")
parser.add_argument("--startNum", "-s", type=int, default=0, help="开始数")
parser.add_argument("--changeType", "-c", type=str,
default='', help="重命名文件为指定类型")
parser.add_argument("--ignoreType", "-i", type=str,
default='', help="忽略处理的类型,使用空格分割")
parser.add_argument("--typeOnly", "-t", type=str,
default='', help="指定处理的类型,使用空格分割")
args = parser.parse_args()
rename_files(dir=args.dir, joiner="_temp_", ignoreType=args.ignoreType, typeOnly=args.typeOnly)
rename_files(dir=args.dir, prefix=args.prefix, joiner=args.joiner, startNum=args.startNum,
changeType=args.changeType, ignoreType=args.ignoreType, typeOnly=args.typeOnly)
print("Rename files finished")
3)添加到数据集
已经切割得到的脸部经过重新排序命名后,按照一定的比例添加到数据集。相关代码如下:
def divide_train_test(src, train_percentage=5, test_percentage=1):
if not os.path.exists(src):
print("folder %s is not exist" % src)
return
dirs = os.listdir(src)
test_dir = os.path.join(src, "test")
train_dir = os.path.join(src, "train") #训练数据路径
if not os.path.exists(test_dir):
os.mkdir(test_dir)
if not os.path.exists(train_dir):
os.mkdir(train_dir)
for dir_name in dirs:
if dir_name != "test" and dir_name != "train":
current_dir = os.path.join(src, dir_name)
test_dir = os.path.join(src, "test", dir_name) #测试集路径
train_dir = os.path.join(src, "train", dir_name) #训练集路径
if not os.path.exists(test_dir):
os.mkdir(test_dir)
if not os.path.exists(train_dir):
os.mkdir(train_dir)
if os.path.isdir(current_dir):
images = os.listdir(current_dir)
image_num = len(images)
for image in images:
filename = os.path.basename(image).split('.')[0]
if filename.isdigit():
percentage = train_percentage + test_percentage
test_num = (image_num / percentage) * test_percentage + 1
if int(filename) <= test_num:
if not os.path.exists(os.path.join(test_dir, image)):
shutil.move(os.path.join(current_dir, image), os.path.join(test_dir))
else:
os.remove(os.path.join(current_dir, image))
else:
if not os.path.exists(os.path.join(train_dir, image)):
shutil.move(os.path.join(current_dir, image), os.path.join(train_dir))
else:
os.remove(os.path.join(current_dir, image))
shutil.rmtree(current_dir)
for dirs in os.listdir(src):
for name in os.listdir(os.path.join(src, dirs)):
if os.path.isdir(os.path.join(src, dirs, name)):
rename_file(os.path.join(src, dirs, name))
print("Set all cropped images to train and test")
3. 模型训练及保存
本部分包括设置基本参数、模型保存和模块预测。
1)设置基本参数
相关代码如下:
DATA_PATH = "Datas" #数据集路径
TRAIN_NUM = 30 #训练次数
BATCH = 5 #批次
model_trainer = ModelTraining()
model_trainer.setModelTypeAsResNet() #训练算法
model_trainer.setDataDirectory(data_path) #训练目录
model_trainer.trainModel(num_objects=num_obj,
#该参数用于指定图像数据集中对象的数量
num_experiments=train_num, #该参数用于指定图像训练的次数
enhance_data=True, #该参数用于指定是否生成训练图像的副本以获得更好的性能
batch_size=batch, #该参数用于指定批次数量,分批训练,直到所有批次训练集都完成为止
show_network_summary=True #该参数用于指定是否在控制台中显示训练的过程
2)模型保存
模型每次训练完成都会输出一个.h5文件和对应的.json文件,如图1所示。model_class.json
文件中包含人物名称,molde_ex-xxx_acc_xxxxxx.h5
中ex后的数字表示训练次数,acc后的数字表示对应的精度。model_class.json
文件中的人物名称如图2所示,采用Unicode编码。训练好的模型保存后可重复使用,也可移植到其他环境中使用。
3)模块预测
相关代码如下:
#设置基本参数
IMAGE_PATH = "uploader/" #预测图片路径
MODEL_PATH = "data/models/model_ex-150_acc-0.883871.h5" #模型路径
JSON_PATH = "data/json/model_class.json" #json文件路径
RESULT_COUNT = 3 #显示预测结果的数量
prediction = CustomImagePrediction() #初始化ResNet
prediction.setModelTypeAsResNet() #设置ResNet模型
#预测函数
def predict(img_path, model_path=MODEL_PATH, json_path=JSON_PATH, result_count=RESULT_COUNT):
if not os.path.exists(img_path):
print("Can not found img %s" % img_path)
return
with open(json_path) as f:
num_obj = len(json.load(f))
print(num_obj)
prediction.setModelPath(model_path)
prediction.setJsonPath(json_path)
prediction.loadModel(num_objects=num_obj)
predictions, probabilities = prediction.predictImage(img_path, result_count=result_count)
result = {}
i = 1
for eachPrediction, eachProbability in zip(predictions, probabilities):
result[i]={eachPrediction: str(round(float(eachProbability), 2)) + '%' }
i = i + 1
print(result)
return result
相关其它博客
基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(一)
基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(二)
基于opencv+ImageAI+tensorflow的智能动漫人物识别系统——深度学习算法应用(含python、JS、模型源码)+数据集(四)
工程源代码下载
其它资料下载
如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载》
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。