{"record":{"id":"6ff1858b3daea088","repo":"deepfakes/faceswap","slug":"spatial-smoothing-only-supports-68-point-facial-la","errorCode":null,"errorMessage":"Spatial smoothing only supports 68 point facial landmarks","messagePattern":"Spatial smoothing only supports 68 point facial landmarks","errorType":"exception","errorClass":"FaceswapError","httpStatus":null,"severity":"error","filePath":"tools/alignments/jobs.py","lineNumber":610,"sourceCode":"        # move back to the correct scale\n        shapes_centered = shapes_normalized * np.tile(scale_factors, [num_pts, num_dims, 1])\n        # move back to the correct location\n        shapes_im_coords = shapes_centered + np.tile(mean_coords, [num_pts, 1, 1])\n\n        logger.debug(\"Normalized to original: %s\", shapes_im_coords)\n        return shapes_im_coords\n\n    def _normalize(self) -> None:\n        \"\"\"Compile all original and normalized alignments\"\"\"\n        logger.debug(\"Normalize\")\n        count = sum(1 for val in self._alignments.data.values() if val.faces)\n\n        sample_lm = next((val.faces[0].landmarks_xy\n                          for val in self._alignments.data.values() if val.faces), 68)\n        assert isinstance(sample_lm, np.ndarray)\n        lm_count = sample_lm.shape[0]\n        if lm_count != 68:\n            raise FaceswapError(\"Spatial smoothing only supports 68 point facial landmarks\")\n\n        landmarks_all = np.zeros((lm_count, 2, int(count)))\n\n        end = 0\n        for key in tqdm(sorted(self._alignments.data.keys()), desc=\"Compiling\", leave=False):\n            val = self._alignments.data[key].faces\n            if not val:\n                continue\n            # We should only be normalizing a single face, so just take\n            # the first landmarks found\n            landmarks = np.array(val[0].landmarks_xy).reshape((lm_count, 2, 1))\n            start = end\n            end = start + landmarks.shape[2]\n            # Store in one big array\n            landmarks_all[:, :, start:end] = landmarks\n            # Make sure we keep track of the mapping to the original frame\n            self._mappings[start] = key\n","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/deepfakes/faceswap/blob/f530cb7508ae670f6474f8a7d9c4df94705cf96b/tools/alignments/jobs.py#L592-L628","documentation":"The Alignments tool's 'spatial' smoothing method builds a (68, 2, N) landmark tensor, so it hard-requires 68-point landmarks. It samples the first aligned face in the file; if that landmark array's first dimension is not 68 (e.g. 4-point 2D landmarks from some detectors/masks), it aborts.","triggerScenarios":"Running `python tools.py alignments -j spatial` on an alignments file whose faces were extracted with a landmark set other than 68-point (e.g. LM_2D_4 produced by certain mask/detector configurations), or where the first face's stored landmarks array has a different row count.","commonSituations":"Alignments produced with newer extraction defaults that store 4-point landmarks for some faces; mixing alignments files from different detector versions; legacy files converted from 81-point formats.","solutions":["Re-extract the faces with a 68-point landmark configuration (standard FAN/dlib S3FD pipeline) so alignments contain 68-point landmarks, then retry spatial smoothing.","Or use a smoothing method that does not depend on landmark count (e.g. temporal smoothing) if it fits your use case.","Inspect the alignments file to confirm landmark shape before choosing the tool job."],"exampleFix":"# before\npython tools.py alignments -j spatial -a alignments.fsa   # faces have 4-point landmarks -> FaceswapError\n\n# after\n# re-extract with 68-point landmarks, then:\npython tools.py alignments -j spatial -a alignments.fsa","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef landmarks_are_68(alignments_file: str) -> bool:\n    from lib.align import Alignments\n    al = Alignments(alignments_file)\n    face = next(v.faces[0] for v in al.data.values() if v.faces)\n    return np.asarray(face.landmarks_xy).shape[0] == 68","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Before running -j spatial, sample one face from the alignments and assert 68 landmark rows.","Standardize extraction settings across the project so landmark counts are uniform."],"tags":["faceswap","alignments","landmarks","smoothing","data-format"],"backgroundTag":null,"analyzedSha":"f530cb7508ae670f6474f8a7d9c4df94705cf96b","analyzedAt":"2026-08-15T02:59:26.626Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}