diff --git a/lab-isic/deepfake_detection_project/.keep b/lab-isic/deepfake_detection_project/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/lab-isic/deepfake_detection_project/df_detect.ipynb b/lab-isic/deepfake_detection_project/df_detect.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8eaad6bd8cee0e50c749391c1cfb65ccf2d4c36a --- /dev/null +++ b/lab-isic/deepfake_detection_project/df_detect.ipynb @@ -0,0 +1,456 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "## DeepFake 检测\n", + "### 1. 解压数据集" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# 解压数据集到\"/home/aistudio/work/\"目录下\n", + "!unzip -d /home/aistudio/work/ /home/aistudio/data/data105515/dataset_video.zip" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "### 2. 视频提帧" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import cv2\n", + "\n", + "\n", + "def video_to_frames(source_path, target_dir, select_num, x):\n", + " # select_num = 1, x = num; select_num = 0, x = interval\n", + " cap = cv2.VideoCapture(source_path)\n", + " if cap.isOpened():\n", + " frame_num = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n", + " # print(frame_num)\n", + " if select_num == 1:\n", + " num = int(x)\n", + " if frame_num >= num:\n", + " interval = frame_num//num\n", + " # print(interval)\n", + " else:\n", + " # print('Error: expect num of frames > actual ...')\n", + " os._exit(0)\n", + " else:\n", + " interval = int(x)\n", + " v_name = os.path.split(source_path)[-1]\n", + " v_name = v_name.split('.')[0]\n", + " # print(v_name)\n", + " for i in range(0, frame_num-1):\n", + " ret, frame = cap.read()\n", + " if ret:\n", + " if i % interval == 0:\n", + " f_path = os.path.join(target_dir, v_name+'_'+str(i)+'.png')\n", + " print(f_path)\n", + " cv2.imwrite(f_path, frame)\n", + " else:\n", + " print('fail to read frame - video_to_face_frames')\n", + " os._exit(0)\n", + " cap.release()\n", + "\n", + "dataset_video = ['work/dataset_video/fake', 'work/dataset_video/real']\n", + "dataset_frame = ['work/dataset_frame/fake', 'work/dataset_frame/real']\n", + "\n", + "for i in range(2):\n", + " for f in os.listdir(dataset_video[i]):\n", + " # 每个视频取 10 帧\n", + " video_to_frames(os.path.join(dataset_video[i], f), dataset_frame[i], 1, 10)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import PIL.Image as Image\n", + "\n", + "path='work/dataset_frame/fake/fake_127_252.png'\n", + "img = Image.open(path)\n", + "plt.imshow(img)\n", + "plt.show() #显示 fake 帧\n", + "\n", + "path='work/dataset_frame/real/real_115_0.png'\n", + "img = Image.open(path)\n", + "plt.imshow(img)\n", + "plt.show() #显示 real 帧" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "### 3. 面部裁剪" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# 下载 pyramidbox_lite_mobile 预训练模型\n", + "!hub install pyramidbox_lite_mobile==1.2.0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import paddlehub as hub\n", + "\n", + "import cv2\n", + "\n", + "import os\n", + "\n", + "\n", + "face_detector = hub.Module(name=\"pyramidbox_lite_mobile\")\n", + "\n", + "frame_dir = ['work/dataset_frame/fake', 'work/dataset_frame/real']\n", + "face_dir = ['work/dataset_face/fake', 'work/dataset_face/real']\n", + "\n", + "for i in range(2):\n", + " paths = os.listdir(frame_dir[i])\n", + " paths = [os.path.join(frame_dir[i], file_name) for file_name in paths]\n", + "\n", + " result = face_detector.face_detection(paths=paths)\n", + " print(result[0])\n", + "\n", + " for res in result:\n", + " if [] != res['data']:\n", + " # 扩大裁剪范围,尽可能包含五官和头发等\n", + " x = (res['data'][0]['right'] - res['data'][0]['left']) // 5\n", + " y = (res['data'][0]['bottom'] - res['data'][0]['top']) // 5\n", + " l = res['data'][0]['left'] - x\n", + " r = res['data'][0]['right'] + x\n", + " t = res['data'][0]['top'] - y\n", + " b = res['data'][0]['bottom'] + y\n", + " im = cv2.imread(res['path'])\n", + " im = im[t:b, l:r]\n", + " _, s_name = os.path.split(res['path'])\n", + " s_path = os.path.join(face_dir[i], s_name)\n", + " cv2.imwrite(s_path, im)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# 删除空白样本(帧上未发现人脸)\n", + "import os\n", + "\n", + "\n", + "face_dir = ['work/dataset_face/fake', 'work/dataset_face/real']\n", + "\n", + "for i in range(2):\n", + " paths = os.listdir(face_dir[i])\n", + " paths = [os.path.join(face_dir[i], file_name) for file_name in paths]\n", + " \n", + " for fpng in paths:\n", + " if 0 == os.path.getsize(fpng):\n", + " os.remove(fpng)\n", + "\n", + "print('fake 样本数:', len(os.listdir(face_dir[0])))\n", + "print('real 样本数:', len(os.listdir(face_dir[1])))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import matplotlib.pyplot as plt\n", + "import PIL.Image as Image\n", + "\n", + "path='work/dataset_face/fake/fake_74_129.png'\n", + "img = Image.open(path)\n", + "plt.imshow(img)\n", + "plt.show() #显示 fake 样本 \n", + "\n", + "path='work/dataset_face/real/real_58_150.png'\n", + "img = Image.open(path)\n", + "plt.imshow(img)\n", + "plt.show() #显示 real 样本" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "### 4. 自定义数据集" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import paddle\n", + "\n", + "import numpy as np\n", + "\n", + "import PIL.Image as Image\n", + "\n", + "from random import shuffle\n", + "\n", + "from paddle.io import Dataset\n", + "\n", + "from paddle.static import InputSpec\n", + "\n", + "from paddle.vision.models import resnet101\n", + "\n", + "from paddle.vision.transforms import Compose, Resize, Transpose, Normalize, RandomHorizontalFlip, RandomRotation\n", + "\n", + "\n", + "fake_list, real_list = [], []\n", + "fake_path = 'work/dataset_face/fake'\n", + "real_path = 'work/dataset_face/real'\n", + "\n", + "for root, dirs, files in os.walk(fake_path):\n", + " for f in files:\n", + " if f.endswith('png'):\n", + " fake_list.append([os.path.join(fake_path, f), int(0)])\n", + "for root, dirs, files in os.walk(real_path):\n", + " for f in files:\n", + " if f.endswith('png'):\n", + " real_list.append([os.path.join(real_path, f), int(1)])\n", + "\n", + "print(len(fake_list))\n", + "print(fake_list[0])\n", + "print(len(real_list))\n", + "print(real_list[0])\n", + "\n", + "data_list = fake_list + real_list\n", + "shuffle(data_list)\n", + "\n", + "def preprocess(img):\n", + " #预处理\n", + " transform = Compose([\n", + " Resize(size=(224,224)),\n", + " Normalize([0.5] * 3, [0.5] * 3, data_format='HWC'),\n", + " RandomHorizontalFlip(0.5),\n", + " RandomRotation(0.5),\n", + " Transpose(),\n", + " ])\n", + " img = transform(img).astype(\"float32\")\n", + " return img\n", + "\n", + "class dfdcDataset(Dataset):\n", + " #自定义dfdc数据集\n", + " def __init__(self, data, is_val=False):\n", + " super().__init__()\n", + " #取20%的样本作为测试集,80%的样本作为训练集。\n", + " self.samples = data[-int(len(data)*0.3):] if is_val else data[:-int(len(data)*0.3)]\n", + "\n", + " def __getitem__(self, idx):\n", + " #处理图像\n", + " img_path = self.samples[idx][0] #得到某样本的路径\n", + " #img = np.array(Image.open(img_path))\n", + " img = Image.open(img_path)\n", + " if img.mode != 'RGB':\n", + " img = img.convert('RGB')\n", + " img = preprocess(img)\n", + "\n", + " #处理标签\n", + " label = self.samples[idx][1] #得到某样本的标签\n", + " label = np.array([label], dtype=\"int64\") #把标签数据类型转成int64\n", + " return img, label\n", + "\n", + " def __len__(self):\n", + " #返回每个Epoch中图片数量\n", + " return len(self.samples)\n", + "\n", + "train_set = dfdcDataset(data_list, is_val=False)\n", + "eval_set = dfdcDataset(data_list, is_val=True)\n", + "\n", + "print(train_set[0][0].shape)\n", + "print(train_set[0][1].shape)\n", + "print(len(train_set))\n", + "print(len(eval_set))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "### 5. 训练模型" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "选择 ResNet101 网络" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# build model \n", + "net = resnet101(pretrained=True)\n", + "net.fc = paddle.nn.Linear(2048, 2)\n", + "# 打印网络结构\n", + "net" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "inputs = InputSpec([None, 3, 224, 224], 'float32', 'x')\n", + "labels = InputSpec([None, 1], 'int64', 'label')\n", + "\n", + "model=paddle.Model(net, inputs, labels)\n", + "\n", + "model.summary((1,3,224,224))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "模型训练" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model.prepare(optimizer=paddle.optimizer.Adam(learning_rate=0.001,parameters=model.parameters()),\n", + " loss=paddle.nn.CrossEntropyLoss(),\n", + " metrics=paddle.metric.Accuracy())\n", + "\n", + "\n", + "model.fit(\n", + " train_data=train_set, \n", + " eval_data=eval_set, \n", + " batch_size=64, \n", + " epochs=20, \n", + " verbose=1, \n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false + }, + "source": [ + "请点击[此处](https://ai.baidu.com/docs#/AIStudio_Project_Notebook/a38e5576)查看本环境基本用法.
\n", + "Please click [here ](https://ai.baidu.com/docs#/AIStudio_Project_Notebook/a38e5576) for more detailed instructions. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "PaddlePaddle 2.1.2 (Python 3.5)", + "language": "python", + "name": "py35-paddle1.2.0" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +}