{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"由專案修改：\nhttps://www.kaggle.com/drjerk/detect-faces-using-yolo\n\n此專案國防大學機器學習由蔡宗憲老師指導的期中報告-DeepFakes換臉辨識挑戰(使用YOLOv2)","metadata":{}},{"cell_type":"markdown","source":"# 1. Load packages","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport matplotlib\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm_notebook\n%matplotlib inline \nimport cv2 as cv\nfrom matplotlib.patches import Rectangle","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:27.777732Z","iopub.execute_input":"2021-10-26T06:52:27.778209Z","iopub.status.idle":"2021-10-26T06:52:27.793054Z","shell.execute_reply.started":"2021-10-26T06:52:27.778133Z","shell.execute_reply":"2021-10-26T06:52:27.791878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2.Load data\n","metadata":{}},{"cell_type":"code","source":"DATA_FOLDER = '../input/deepfake-detection-challenge'\nTRAIN_SAMPLE_FOLDER = 'train_sample_videos'\nTEST_FOLDER = 'test_videos'\n\nprint(f\"Train samples: {len(os.listdir(os.path.join(DATA_FOLDER, TRAIN_SAMPLE_FOLDER)))}\")\nprint(f\"Test samples: {len(os.listdir(os.path.join(DATA_FOLDER, TEST_FOLDER)))}\")","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:27.795645Z","iopub.execute_input":"2021-10-26T06:52:27.79627Z","iopub.status.idle":"2021-10-26T06:52:27.810512Z","shell.execute_reply.started":"2021-10-26T06:52:27.796207Z","shell.execute_reply":"2021-10-26T06:52:27.809846Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 確認一下訓練樣本及測試樣版中的副檔名。","metadata":{}},{"cell_type":"code","source":"train_list = list(os.listdir(os.path.join(DATA_FOLDER, TRAIN_SAMPLE_FOLDER)))\next_dict = []\nfor file in train_list:\n    file_ext = file.split('.')[1]\n    if (file_ext not in ext_dict):\n        ext_dict.append(file_ext)\nprint(f\"Extensions: {ext_dict}\")     \n\n\nfor file_ext in ext_dict:\n    print(f\"Files with extension `{file_ext}`: {len([file for file in train_list if  file.endswith(file_ext)])}\")","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:27.812379Z","iopub.execute_input":"2021-10-26T06:52:27.812893Z","iopub.status.idle":"2021-10-26T06:52:27.824092Z","shell.execute_reply.started":"2021-10-26T06:52:27.812761Z","shell.execute_reply":"2021-10-26T06:52:27.823114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"驗證訓練檔案中，JSON 檔的內容：","metadata":{}},{"cell_type":"code","source":"json_file = [file for file in train_list if  file.endswith('json')][0]\nprint(f\"JSON file: {json_file}\")\n\ndef get_meta_from_json(path):\n    df = pd.read_json(os.path.join(DATA_FOLDER, path, json_file))\n    df = df.T\n    return df\n\nmeta_train_df = get_meta_from_json(TRAIN_SAMPLE_FOLDER)\nmeta_train_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:27.825679Z","iopub.execute_input":"2021-10-26T06:52:27.826167Z","iopub.status.idle":"2021-10-26T06:52:28.031325Z","shell.execute_reply.started":"2021-10-26T06:52:27.826124Z","shell.execute_reply":"2021-10-26T06:52:28.030571Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"meta_train_df","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.034531Z","iopub.execute_input":"2021-10-26T06:52:28.034823Z","iopub.status.idle":"2021-10-26T06:52:28.051265Z","shell.execute_reply.started":"2021-10-26T06:52:28.034774Z","shell.execute_reply":"2021-10-26T06:52:28.050601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def unique_values(data):\n    total = data.count()\n    tt = pd.DataFrame(total)\n    tt.columns = ['Total']\n    uniques = []\n    for col in data.columns:\n        unique = data[col].nunique()\n        uniques.append(unique)\n    tt['Uniques'] = uniques\n    return(np.transpose(tt))\n\n\nunique_values(meta_train_df)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.053137Z","iopub.execute_input":"2021-10-26T06:52:28.053491Z","iopub.status.idle":"2021-10-26T06:52:28.073853Z","shell.execute_reply.started":"2021-10-26T06:52:28.053453Z","shell.execute_reply":"2021-10-26T06:52:28.073004Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"我們觀察到原始標籤對於唯一值具有相同的模式。 \n我們知道我們有 77 個缺失數據（這就是為什麼總數只有 323 個）並且我們觀察到我們確實有 209 個獨特的例子。","metadata":{}},{"cell_type":"code","source":"def plot_count(feature, title, df, size=1):\n    '''\n    Plot count of classes / feature\n    param: feature - the feature to analyze\n    param: title - title to add to the graph\n    param: df - dataframe from which we plot feature's classes distribution \n    param: size - default 1.\n    '''\n    f, ax = plt.subplots(1,1, figsize=(4*size,4))\n    total = float(len(df))\n    g = sns.countplot(df[feature], order = df[feature].value_counts().index[:20], palette='Set3')\n    g.set_title(\"Number and percentage of {}\".format(title))\n    if(size > 2):\n        plt.xticks(rotation=90, size=8)\n    for p in ax.patches:\n        height = p.get_height()\n        ax.text(p.get_x()+p.get_width()/2.,\n                height + 3,\n                '{:1.2f}%'.format(100*height/total),\n                ha=\"center\") \n    plt.show()    \n\nplot_count('label', 'label (train)', meta_train_df)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.075128Z","iopub.execute_input":"2021-10-26T06:52:28.07551Z","iopub.status.idle":"2021-10-26T06:52:28.316304Z","shell.execute_reply.started":"2021-10-26T06:52:28.07547Z","shell.execute_reply":"2021-10-26T06:52:28.315113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def most_frequent_values(data):\n    total = data.count()\n    tt = pd.DataFrame(total)\n    tt.columns = ['Total']\n    items = []\n    vals = []\n    for col in data.columns:\n        itm = data[col].value_counts().index[0]\n        val = data[col].value_counts().values[0]\n        items.append(itm)\n        vals.append(val)\n    tt['Most frequent item'] = items\n    tt['Frequence'] = vals\n    tt['Percent from total'] = np.round(vals / total * 100, 3)\n    return(np.transpose(tt))\n\nmost_frequent_values(meta_train_df)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.319712Z","iopub.execute_input":"2021-10-26T06:52:28.32013Z","iopub.status.idle":"2021-10-26T06:52:28.374927Z","shell.execute_reply.started":"2021-10-26T06:52:28.32006Z","shell.execute_reply":"2021-10-26T06:52:28.373891Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"最頻繁的標籤是 FAKE（80.75%），atvmxvwyns.mp4 是最頻繁的原始標籤（6 個樣本）。","metadata":{}},{"cell_type":"code","source":"from IPython.display import HTML\nfrom base64 import b64encode\n\ndef play_video(video_file, subset=TRAIN_SAMPLE_FOLDER):\n    '''\n    Display video\n    param: video_file - the name of the video file to display\n    param: subset - the folder where the video file is located (can be TRAIN_SAMPLE_FOLDER or TEST_Folder)\n    '''\n    video_url = open(os.path.join(DATA_FOLDER, subset,video_file),'rb').read()\n    data_url = \"data:video/mp4;base64,\" + b64encode(video_url).decode()\n    return HTML(\"\"\"<video width=500 controls><source src=\"%s\" type=\"video/mp4\"></video>\"\"\" % data_url)\n","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.37672Z","iopub.execute_input":"2021-10-26T06:52:28.377291Z","iopub.status.idle":"2021-10-26T06:52:28.385388Z","shell.execute_reply.started":"2021-10-26T06:52:28.377231Z","shell.execute_reply":"2021-10-26T06:52:28.384767Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.python.keras.layers import Conv2D, Input, ZeroPadding2D, Dense, Lambda\nfrom tensorflow.python.keras.models import Model\nfrom tensorflow.python.keras.applications.mobilenet_v2 import MobileNetV2\nimport tensorflow as tf\ntf.compat.v1.disable_eager_execution()\nimport math\nimport numpy as np\nimport cv2","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.386574Z","iopub.execute_input":"2021-10-26T06:52:28.386897Z","iopub.status.idle":"2021-10-26T06:52:28.399107Z","shell.execute_reply.started":"2021-10-26T06:52:28.386842Z","shell.execute_reply":"2021-10-26T06:52:28.398339Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_mobilenetv2_224_075_detector(path):\n    input_tensor = Input(shape=(224, 224, 3))\n    output_tensor = MobileNetV2(weights=None, include_top=False, input_tensor=input_tensor, alpha=0.75).output\n    output_tensor = ZeroPadding2D()(output_tensor)\n    output_tensor = Conv2D(kernel_size=(3, 3), filters=5)(output_tensor)\n\n    model = Model(inputs=input_tensor, outputs=output_tensor)\n    model.load_weights(path)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.400254Z","iopub.execute_input":"2021-10-26T06:52:28.400611Z","iopub.status.idle":"2021-10-26T06:52:28.415327Z","shell.execute_reply.started":"2021-10-26T06:52:28.400574Z","shell.execute_reply":"2021-10-26T06:52:28.414276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 載入已訓練過的最佳模型(Optimal Model)\n使用已受訓練的樣本特徵資料集：facedetection-mobilenetv2-size224-alpha0.75.h5","metadata":{}},{"cell_type":"code","source":"mobilenetv2 = load_mobilenetv2_224_075_detector(\"../input/facedetection-mobilenetv2/facedetection-mobilenetv2-size224-alpha0.75.h5\")\nmobilenetv2.summary()","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:28.416724Z","iopub.execute_input":"2021-10-26T06:52:28.417144Z","iopub.status.idle":"2021-10-26T06:52:37.110563Z","shell.execute_reply.started":"2021-10-26T06:52:28.417088Z","shell.execute_reply":"2021-10-26T06:52:37.109752Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"樣本中顯示：\n本次比賽中有 1920x1080 (16:9) 和 1080x1920 (9:16) 圖像\n（如果有發現其他圖像，可以輕鬆地將它們分別添加到 SHOTS 和 SHOTS_T 向量中）\n\n模型是在 1:1 縱橫比圖像上訓練。\n所以如果我們想使用 16:9 和 9:16 圖像，我們需要將它們分成 2 部分，我們也可以將它們分成更小的（例如 10）相交部分以獲得更多對較小臉部的準確預測。","metadata":{}},{"cell_type":"markdown","source":"# 用於檢測人臉的 API：FaceDetector():\n\n__init__ 參數說明：\n\nmodel - 要推斷的模型\n\nshots - 圖像可能的縱橫比列表（之前描述過）\n\nimage_size - 模型的輸入大小（為 mobilenetv2 硬編碼）\n\ngrids - 模型的輸出大小（為 mobilenetv2 硬編碼）\n\nunion_threshold - 多個鏡頭中預測框並集的閾值\n\niou_threshold - 非最大抑制的 IOU 閾值，用於將 YOLO 檢測到的框合併為一個鏡頭，\n                您確實需要更改此設置，預期每張圖像都有至少有一張臉\n                \nprob_threshold - YOLO 算法的概率閾值，您可以使用此閾值平衡精度和召回率\n\n    \n檢測參數：\n-------------------------------\nframe - (1920, 1080, 3) or (1080, 1920, 3) RGB Image\nreturns: list of 4 element tuples (left corner x, left corner y, right corner x, right corner y) of detected boxes within [0, 1] range (see box draw code below)\n","metadata":{}},{"cell_type":"code","source":"# Converts A:B aspect rate to B:A\ndef transpose_shots(shots):\n    return [(shot[1], shot[0], shot[3], shot[2], shot[4]) for shot in shots]\n\n#That constant describe pieces for 16:9 images\nSHOTS = {\n    # fast less accurate\n    '2-16/9' : {\n        'aspect_ratio' : 16/9,\n        'shots' : [\n             (0, 0, 9/16, 1, 1),\n             (7/16, 0, 9/16, 1, 1)\n        ]\n    },\n    # slower more accurate\n    '10-16/9' : {\n        'aspect_ratio' : 16/9,\n        'shots' : [\n             (0, 0, 9/16, 1, 1),\n             (7/16, 0, 9/16, 1, 1),\n             (0, 0, 5/16, 5/9, 0.5),\n             (0, 4/9, 5/16, 5/9, 0.5),\n             (11/48, 0, 5/16, 5/9, 0.5),\n             (11/48, 4/9, 5/16, 5/9, 0.5),\n             (22/48, 0, 5/16, 5/9, 0.5),\n             (22/48, 4/9, 5/16, 5/9, 0.5),\n             (11/16, 0, 5/16, 5/9, 0.5),\n             (11/16, 4/9, 5/16, 5/9, 0.5),\n        ]\n    }\n}\n\n# 9:16 respectively\nSHOTS_T = {\n    '2-9/16' : {\n        'aspect_ratio' : 9/16,\n        'shots' : transpose_shots(SHOTS['2-16/9']['shots'])\n    },\n    '10-9/16' : {\n        'aspect_ratio' : 9/16,\n        'shots' : transpose_shots(SHOTS['10-16/9']['shots'])\n    }\n}\n\ndef r(x):\n    return int(round(x))\n\ndef sigmoid(x):\n    return 1 / (np.exp(-x) + 1)\n\ndef non_max_suppression(boxes, p, iou_threshold):\n\n    if len(boxes) == 0:\n        return np.array([])\n\n    x1 = boxes[:, 0]\n    y1 = boxes[:, 1]\n    x2 = boxes[:, 2]\n    y2 = boxes[:, 3]\n\n    indexes = np.argsort(p)\n    true_boxes_indexes = []\n\n    while len(indexes) > 0:\n        true_boxes_indexes.append(indexes[-1])\n\n        intersection = np.maximum(np.minimum(x2[indexes[:-1]], x2[indexes[-1]]) - np.maximum(x1[indexes[:-1]], x1[indexes[-1]]), 0) * np.maximum(np.minimum(y2[indexes[:-1]], y2[indexes[-1]]) - np.maximum(y1[indexes[:-1]], y1[indexes[-1]]), 0)\n        iou = intersection / ((x2[indexes[:-1]] - x1[indexes[:-1]]) * (y2[indexes[:-1]] - y1[indexes[:-1]]) + (x2[indexes[-1]] - x1[indexes[-1]]) * (y2[indexes[-1]] - y1[indexes[-1]]) - intersection)\n\n        indexes = np.delete(indexes, -1)\n        indexes = np.delete(indexes, np.where(iou >= iou_threshold)[0])\n\n    return boxes[true_boxes_indexes]\n\ndef union_suppression(boxes, threshold):\n    if len(boxes) == 0:\n        return np.array([])\n\n    x1 = boxes[:, 0]\n    y1 = boxes[:, 1]\n    x2 = boxes[:, 2]\n    y2 = boxes[:, 3]\n\n    indexes = np.argsort((x2 - x1) * (y2 - y1))\n    result_boxes = []\n\n    while len(indexes) > 0:\n        intersection = np.maximum(np.minimum(x2[indexes[:-1]], x2[indexes[-1]]) - np.maximum(x1[indexes[:-1]], x1[indexes[-1]]), 0) * np.maximum(np.minimum(y2[indexes[:-1]], y2[indexes[-1]]) - np.maximum(y1[indexes[:-1]], y1[indexes[-1]]), 0)\n        min_s = np.minimum((x2[indexes[:-1]] - x1[indexes[:-1]]) * (y2[indexes[:-1]] - y1[indexes[:-1]]), (x2[indexes[-1]] - x1[indexes[-1]]) * (y2[indexes[-1]] - y1[indexes[-1]]))\n        ioms = intersection / (min_s + 1e-9)\n        neighbours = np.where(ioms >= threshold)[0]\n        if len(neighbours) > 0:\n            result_boxes.append([min(np.min(x1[indexes[neighbours]]), x1[indexes[-1]]), min(np.min(y1[indexes[neighbours]]), y1[indexes[-1]]), max(np.max(x2[indexes[neighbours]]), x2[indexes[-1]]), max(np.max(y2[indexes[neighbours]]), y2[indexes[-1]])])\n        else:\n            result_boxes.append([x1[indexes[-1]], y1[indexes[-1]], x2[indexes[-1]], y2[indexes[-1]]])\n\n        indexes = np.delete(indexes, -1)\n        indexes = np.delete(indexes, neighbours)\n\n    return result_boxes\n\nclass FaceDetector():\n    \"\"\"\n    That's API you can easily use to detect faces\n    \n    __init__ parameters:\n    -------------------------------\n    model - model to infer\n    shots - list of aspect ratios that images could be (described earlier)\n    image_size - model's input size (hardcoded for mobilenetv2)\n    grids - model's output size (hardcoded for mobilenetv2)\n    union_threshold - threshold for union of predicted boxes within multiple shots\n    iou_threshold - IOU threshold for non maximum suppression used to merge YOLO detected boxes for one shot,\n                    you do need to change this because there are one face per image as I can see from the samples\n    prob_threshold - probability threshold for YOLO algorithm, you can balance beetween precision and recall using this threshold\n    \n    detect parameters:\n    -------------------------------\n    frame - (1920, 1080, 3) or (1080, 1920, 3) RGB Image\n    returns: list of 4 element tuples (left corner x, left corner y, right corner x, right corner y) of detected boxes within [0, 1] range (see box draw code below)\n    \"\"\"\n    def __init__(self, model=mobilenetv2, shots=[SHOTS['10-16/9'], SHOTS_T['10-9/16']], image_size=224, grids=7, iou_threshold=0.1, union_threshold=0.1, prob_threshold=0.65):\n        self.model = model\n        self.shots = shots\n        self.image_size = image_size\n        self.grids = grids\n        self.iou_threshold = iou_threshold\n        self.union_threshold = union_threshold\n        self.prob_threshold = prob_threshold\n        \n    \n    def detect(self, frame):\n        original_frame_shape = frame.shape\n\n        aspect_ratio = None\n        for shot in self.shots:\n            if abs(frame.shape[1] / frame.shape[0] - shot[\"aspect_ratio\"]) < 1e-9:\n                aspect_ratio = shot[\"aspect_ratio\"]\n                shots = shot\n        \n        assert aspect_ratio is not None\n        \n        c = min(frame.shape[0], frame.shape[1] / aspect_ratio)\n        slice_h_shift = r((frame.shape[0] - c) / 2)\n        slice_w_shift = r((frame.shape[1] - c * aspect_ratio) / 2)\n        if slice_w_shift != 0 and slice_h_shift == 0:\n            frame = frame[:, slice_w_shift:-slice_w_shift]\n        elif slice_w_shift == 0 and slice_h_shift != 0:\n            frame = frame[slice_h_shift:-slice_h_shift, :]\n\n        frames = []\n        for s in shots[\"shots\"]:\n            frames.append(cv2.resize(frame[r(s[1] * frame.shape[0]):r((s[1] + s[3]) * frame.shape[0]), r(s[0] * frame.shape[1]):r((s[0] + s[2]) * frame.shape[1])], (self.image_size, self.image_size), interpolation=cv2.INTER_NEAREST))\n        frames = np.array(frames)\n\n        predictions = self.model.predict(frames, batch_size=len(frames), verbose=0)\n\n        boxes = []\n        prob = []\n        shots = shots['shots']\n        for i in range(len(shots)):\n            slice_boxes = []\n            slice_prob = []\n            for j in range(predictions.shape[1]):\n                for k in range(predictions.shape[2]):\n                    p = sigmoid(predictions[i][j][k][4])\n                    if not(p is None) and p > self.prob_threshold:\n                        px = sigmoid(predictions[i][j][k][0])\n                        py = sigmoid(predictions[i][j][k][1])\n                        pw = min(math.exp(predictions[i][j][k][2] / self.grids), self.grids)\n                        ph = min(math.exp(predictions[i][j][k][3] / self.grids), self.grids)\n                        if not(px is None) and not(py is None) and not(pw is None) and not(ph is None) and pw > 1e-9 and ph > 1e-9:\n                            cx = (px + j) / self.grids\n                            cy = (py + k) / self.grids\n                            wx = pw / self.grids\n                            wy = ph / self.grids\n                            if wx <= shots[i][4] and wy <= shots[i][4]:\n                                lx = min(max(cx - wx / 2, 0), 1)\n                                ly = min(max(cy - wy / 2, 0), 1)\n                                rx = min(max(cx + wx / 2, 0), 1)\n                                ry = min(max(cy + wy / 2, 0), 1)\n\n                                lx *= shots[i][2]\n                                ly *= shots[i][3]\n                                rx *= shots[i][2]\n                                ry *= shots[i][3]\n\n                                lx += shots[i][0]\n                                ly += shots[i][1]\n                                rx += shots[i][0]\n                                ry += shots[i][1]\n\n                                slice_boxes.append([lx, ly, rx, ry])\n                                slice_prob.append(p)\n\n            slice_boxes = np.array(slice_boxes)\n            slice_prob = np.array(slice_prob)\n\n            slice_boxes = non_max_suppression(slice_boxes, slice_prob, self.iou_threshold)\n\n            for sb in slice_boxes:\n                boxes.append(sb)\n\n\n        boxes = np.array(boxes)\n        boxes = union_suppression(boxes, self.union_threshold)\n\n        for i in range(len(boxes)):\n            boxes[i][0] /= original_frame_shape[1] / frame.shape[1]\n            boxes[i][1] /= original_frame_shape[0] / frame.shape[0]\n            boxes[i][2] /= original_frame_shape[1] / frame.shape[1]\n            boxes[i][3] /= original_frame_shape[0] / frame.shape[0]\n\n            boxes[i][0] += slice_w_shift / original_frame_shape[1]\n            boxes[i][1] += slice_h_shift / original_frame_shape[0]\n            boxes[i][2] += slice_w_shift / original_frame_shape[1]\n            boxes[i][3] += slice_h_shift / original_frame_shape[0]\n\n        return list(boxes)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:37.111999Z","iopub.execute_input":"2021-10-26T06:52:37.112247Z","iopub.status.idle":"2021-10-26T06:52:37.195735Z","shell.execute_reply.started":"2021-10-26T06:52:37.112205Z","shell.execute_reply":"2021-10-26T06:52:37.194823Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* 檢測器程式碼的結尾\n\n在模型訓練完成後，最終需要可以即時偵測到人臉後辨識：","metadata":{}},{"cell_type":"code","source":"detector = FaceDetector()","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:37.197861Z","iopub.execute_input":"2021-10-26T06:52:37.198395Z","iopub.status.idle":"2021-10-26T06:52:37.213602Z","shell.execute_reply.started":"2021-10-26T06:52:37.198309Z","shell.execute_reply":"2021-10-26T06:52:37.213027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_FOLDER = '../input/deepfake-detection-challenge'\nTRAIN_SAMPLE_FOLDER = 'train_sample_videos'\nTEST_FOLDER = 'test_videos'\n\ntrain_list = list(os.listdir(os.path.join(DATA_FOLDER, TRAIN_SAMPLE_FOLDER)))\njson_file = [file for file in train_list if  file.endswith('json')][0]\n\ndef get_meta_from_json(path):\n    df = pd.read_json(os.path.join(DATA_FOLDER, path, json_file))\n    df = df.T\n    return df\n\nmeta_train_df = get_meta_from_json(TRAIN_SAMPLE_FOLDER)","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:52:37.21448Z","iopub.execute_input":"2021-10-26T06:52:37.214789Z","iopub.status.idle":"2021-10-26T06:52:37.400927Z","shell.execute_reply.started":"2021-10-26T06:52:37.2147Z","shell.execute_reply":"2021-10-26T06:52:37.400079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 先設定載入影片時的畫面長寬與一些影像等原始設定。","metadata":{}},{"cell_type":"code","source":"def display_image_from_video(video_path):\n    '''\n    input: video_path - path for video\n    process:\n    1. perform a video capture from the video\n    2. read the image\n    3. display the image\n    '''\n    capture_image = cv.VideoCapture(video_path) \n    ret, frame = capture_image.read()\n    fig = plt.figure(figsize=(10,10))\n    ax = fig.add_subplot(111)\n    frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)\n    ax.imshow(frame)\n    boxes = detector.detect(frame)\n    # lets's draw boxes, just multiply each predicted [0, 1] relative coordinate to image side in pixels respectively\n    for box in boxes:\n        lx = int(round(box[0] * frame.shape[1]))\n        ly = int(round(box[1] * frame.shape[0]))\n        rx = int(round(box[2] * frame.shape[1]))\n        ry = int(round(box[3] * frame.shape[0]))\n        # x, y, w, h here\n        ax.add_patch(Rectangle((lx,ly),rx - lx,ry - ly,linewidth=2,edgecolor='r',facecolor='none'))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-10-26T06:52:37.402522Z","iopub.execute_input":"2021-10-26T06:52:37.403046Z","iopub.status.idle":"2021-10-26T06:52:37.414562Z","shell.execute_reply.started":"2021-10-26T06:52:37.40275Z","shell.execute_reply":"2021-10-26T06:52:37.413349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 讀取影像前的預處理\n將影像處理為易於識別的格式，識別進行人臉偵測的圖片，如果成功偵測到人臉就框選出人臉。","metadata":{}},{"cell_type":"markdown","source":"# 偽造臉部清單測試\nfake_train_sample_video = list(meta_train_df.loc[meta_train_df.label=='FAKE'].sample(300).index)\nfake_train_sample_video\n\n或用影片來確認：\n\nplay_video(\"eudeqjhdfd.mp4\")\n\nplay_video(\"eukvucdetx.mp4\")","metadata":{}},{"cell_type":"code","source":"fake_train_sample_video = list(meta_train_df.loc[meta_train_df.label=='FAKE'].sample(30).index)\nprint(meta_train_df.loc[meta_train_df.label=='FAKE'].sample(20).index)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-10-26T06:52:37.416122Z","iopub.execute_input":"2021-10-26T06:52:37.416413Z","iopub.status.idle":"2021-10-26T06:52:37.434895Z","shell.execute_reply.started":"2021-10-26T06:52:37.416363Z","shell.execute_reply":"2021-10-26T06:52:37.433461Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for video_file in fake_train_sample_video:\n    display_image_from_video(os.path.join(DATA_FOLDER, TRAIN_SAMPLE_FOLDER, video_file))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-10-26T06:52:37.436245Z","iopub.execute_input":"2021-10-26T06:52:37.43656Z","iopub.status.idle":"2021-10-26T06:53:04.336622Z","shell.execute_reply.started":"2021-10-26T06:52:37.436503Z","shell.execute_reply":"2021-10-26T06:53:04.335603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 真實影片清單","metadata":{}},{"cell_type":"code","source":"real_train_sample_video = list(meta_train_df.loc[meta_train_df.label=='REAL'].sample(30).index)\n\nprint(meta_train_df.loc[meta_train_df.label=='REAL'].sample(20).index)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-10-26T06:53:04.338154Z","iopub.execute_input":"2021-10-26T06:53:04.338427Z","iopub.status.idle":"2021-10-26T06:53:04.348842Z","shell.execute_reply.started":"2021-10-26T06:53:04.338377Z","shell.execute_reply":"2021-10-26T06:53:04.347497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 比較真實 / 偽造影片","metadata":{}},{"cell_type":"code","source":"play_video(\"ehtdtkmmli.mp4\")","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:53:04.350117Z","iopub.execute_input":"2021-10-26T06:53:04.350371Z","iopub.status.idle":"2021-10-26T06:53:04.477089Z","shell.execute_reply.started":"2021-10-26T06:53:04.35033Z","shell.execute_reply":"2021-10-26T06:53:04.475904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"play_video(\"eukvucdetx.mp4\")","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:53:04.478588Z","iopub.execute_input":"2021-10-26T06:53:04.47888Z","iopub.status.idle":"2021-10-26T06:53:04.547882Z","shell.execute_reply.started":"2021-10-26T06:53:04.478833Z","shell.execute_reply":"2021-10-26T06:53:04.545956Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for video_file in real_train_sample_video:\n    display_image_from_video(os.path.join(DATA_FOLDER, TRAIN_SAMPLE_FOLDER, video_file))","metadata":{"execution":{"iopub.status.busy":"2021-10-26T06:53:04.549534Z","iopub.execute_input":"2021-10-26T06:53:04.549917Z","iopub.status.idle":"2021-10-26T06:53:30.517388Z","shell.execute_reply.started":"2021-10-26T06:53:04.549812Z","shell.execute_reply":"2021-10-26T06:53:30.516462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* 訓練結果：可以透過REAL屬性及FAKE精確取得人臉辨識結果。\n\n","metadata":{}}]}