{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"# Keep running into Error: Notebook Exceeded Allowed Compute\n\n* This inference kernel takes at most 7.5G RAM and 4G GPU RAM even if I iterate over 10 times of sample test videos.\n* And by pympler, I had verified that there's no memory leak in code.\n* What's interesting is that resource usage of facenet-pytorch is depends on number of person in the video. I wonder if there's a extreme example in real test video."},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"!pip install ../input/facenet-pytorch-vggface2/facenet_pytorch-2.0.1-py3-none-any.whl\n\nimport sys, os\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport cv2\nimport glob\nimport time\nimport torch\nimport random\nimport time\nfrom PIL import Image\nfrom facenet_pytorch import MTCNN, InceptionResnetV1, extract_face\nfrom torchvision.transforms import Normalize, RandomHorizontalFlip, ToTensor, ToPILImage, Compose, Resize\nfrom sklearn.metrics import log_loss\nimport pathlib\nfrom tqdm import tqdm\nfrom torch.utils.data import DataLoader, Dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Video_reader:\n    def extract_video(self, video_path):\n        cap = cv2.VideoCapture(video_path)\n        frames = []\n        while(cap.isOpened()):\n            ret, frame = cap.read()\n            if ret==True:\n                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n                frames.append(frame)\n                if cv2.waitKey(1) & 0xFF == ord('q'):\n                    break\n            else:\n                break\n        cap.release()\n        assert len(frames) != 0\n        return np.array(frames)\n        \n        \n    def extract_one_frame(self, video_path, frame_index):\n        cap = cv2.VideoCapture(video_path)\n        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index)  #设置要获取的帧号\n        _, frame=cap.read()\n        cap.release()\n        if _:\n            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n            return frame\n        else:\n            return None\n\nclass Face_extractor:\n    def __init__(self):\n        pass\n        \n    def _get_boundingbox(self, bbox, width, height, scale=1.2, minsize=None):\n        x1, y1, x2, y2 = bbox[:4]\n        if not 0.33 < (x2-x1)/(y2-y1) < 3:\n            return np.array([0,0,0,0])\n        size_bb = int(max(x2 - x1, y2 - y1) * scale)\n        if minsize:\n            if size_bb < minsize:\n                size_bb = minsize\n        center_x, center_y = (x1 + x2) // 2, (y1 + y2) // 2\n\n        x1 = max(int(center_x - size_bb / 2), 0)\n        y1 = max(int(center_y - size_bb / 2), 0)\n        size_bb = min(width - x1, size_bb)\n        size_bb = min(height - y1, size_bb)\n\n        return np.array([x1,y1,x1+size_bb,y1+size_bb]).astype(int)\n    \n    \n    def _rectang_crop(self, image, bbox):\n        height, width = image.shape[:2]\n        l,t,r,b = self._get_boundingbox(bbox, width, height) \n        return image[t:b, l:r]\n    \n    def _get(images):\n        pass\n    \n    \n    def get_faces(self, images, with_person_num = False, only_one = True):\n        faces, nums = self._get(images)\n        if only_one:\n            faces = [face[0] for face in faces if len(face)>0]\n            nums = [num for num, face in zip(nums,faces) if len(face)>0]\n        if with_person_num:\n            faces = (faces, nums)\n        return faces\n    \n    \n    def get_face(self, image, with_person_num = False, only_one = True):\n        faces, nums = self.get_faces(np.array([image]), with_person_num=True, only_one=False)\n        faces, nums = faces[0], nums[0]\n        if only_one:\n            if len(faces)>0:\n                faces = faces[0]\n            else:\n                faces = None\n        if with_person_num:\n            faces = (faces, nums)\n        return faces\n    \n    \nclass MTCNN_extractor(Face_extractor):\n    def __init__(self, device = 'cuda:0' if torch.cuda.is_available() else 'cpu', down_sample = 2):\n        self.extractor = MTCNN(keep_all=True, device=device, min_face_size=80//down_sample).eval()\n        self.down_sample = down_sample\n            \n    def _get(self, images):\n        h, w = images.shape[1:3]\n        pils = [Image.fromarray(img).resize((w//self.down_sample, h//self.down_sample)) for img in images]\n        bboxes, probs = self.extractor.detect(pils)\n        \n        facelist = [[self._rectang_crop(img, box) for box in boxes*self.down_sample] for boxes, img in zip(bboxes,images) if boxes is not None]\n        person_nums = [np.sum(prob>0.9) for prob,fss in zip(probs, bboxes) if fss is not None]\n        \n        assert len(person_nums) == len(facelist)\n        return facelist, person_nums\n    \n    \nclass Inference_model:\n    def __init__(self):\n        pass\n        \n    \n    def data_transform(self):\n        # transform to Tensor\n        pre_trained_mean, pre_trained_std = [0.439, 0.328, 0.304], [0.232, 0.206, 0.201]\n        return Compose([Resize(224), ToTensor(), Normalize(pre_trained_mean, pre_trained_std)])\n    \n    \n    def TTA(self, pil_img):\n        return [pil_img, RandomHorizontalFlip(p=1)(pil_img)]\n    \n    \n    def predict(self, batch):\n        return 0.5\n    \n    def test(self, shape = (1,3,224,224)):\n        return self.predict(torch.rand(shape))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict_on_all(video_paths, video_lables = [], video_reader=None, \n                   face_extractor=None, models=None, sample_number = 13):\n    if video_reader is None:\n        video_reader = Video_reader()\n    if face_extractor is None:\n        face_extractor = MTCNN_extractor()\n    if models is None:\n        models = [Inference_model()]\n\n    def predict_one_video(file_path):\n        with torch.no_grad():\n            try:\n                import time\n                start = time.time()\n                frames = video_reader.extract_video(file_path)\n                sample = np.linspace(0, len(frames) - 1, sample_number*2).round().astype(int)\n                faces = face_extractor.get_faces(frames[sample])\n                np.random.shuffle(faces)\n                if len(faces) == 0:\n                    print(\"no face detected\")\n                    return 0.5\n                pils = [Image.fromarray(face)  for face in faces[:sample_number] ]\n\n                answers = []\n                for model in models:\n                    tr = model.data_transform()\n                    batch = torch.stack([tr(p) for img in pils for p in model.TTA(img)])\n                    answers.append(model.predict(batch))\n                return np.mean(answers)\n            except Exception as e:\n                print(\"Error with \", file_path, e)\n                return 0.5\n\n    predicts = [predict_one_video(i) for i in tqdm(video_paths)]\n    \n    if len(video_lables) == len(video_paths):\n        print(f\"loss = {log_loss(video_lables, predicts, labels=[0,1])}\")\n    return predicts","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"kaggle = True\nspeed_test = True\nprint(f\"using {'cuda:0' if torch.cuda.is_available() else 'cpu'}\")\n\nfilenames = glob.glob('/kaggle/input/deepfake-detection-challenge/test_videos/*.mp4')\nlabels = []\n\nmodels = [Inference_model()]\nface_extractor = MTCNN_extractor()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# from pympler.tracker import SummaryTracker\n# tracker = SummaryTracker()\n\nif speed_test:\n    testnum = 5\n    start = time.time()\n    test_filenames, test_labels = filenames[:testnum], []\n    ret = predict_on_all(test_filenames, video_lables=test_labels, models=models, face_extractor=face_extractor)\n    time_dur = time.time()-start\n    print(f\"totally {time_dur} s used, {time_dur/testnum} s per video\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions = predict_on_all(filenames, video_lables=labels, models=models, face_extractor=face_extractor)\nprint(np.mean(predictions))\n\nsubmission_df = pd.DataFrame({\"filename\": [fn.split('/')[-1] for fn in filenames], \"label\": predictions})\nsubmission_df.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":1}