diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..e4f40ad --- /dev/null +++ b/flake.lock @@ -0,0 +1,46 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1760164275, + "narHash": "sha256-gKl2Gtro/LNf8P+4L3S2RsZ0G390ccd5MyXYrTdMCFE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "362791944032cb532aabbeed7887a441496d5e6e", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "pyproject-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759739877, + "narHash": "sha256-XfcxM4nzSuuRmFGiy/MhGwYf9EennQ2+0jjR2aJqtKE=", + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "rev": "966e0961f9670f847439ba90dc25ffaa112d8803", + "type": "github" + }, + "original": { + "owner": "pyproject-nix", + "repo": "pyproject.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "pyproject-nix": "pyproject-nix" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..832cf8c --- /dev/null +++ b/flake.nix @@ -0,0 +1,41 @@ +{ + description = "Flake using pyproject.toml metadata"; + + inputs.pyproject-nix.url = "github:pyproject-nix/pyproject.nix"; + inputs.pyproject-nix.inputs.nixpkgs.follows = "nixpkgs"; + + outputs = + { nixpkgs, pyproject-nix, ... }: + let + inherit (nixpkgs) lib; + forAllSystems = lib.genAttrs lib.systems.flakeExposed; + + project = pyproject-nix.lib.project.loadPyproject { + projectRoot = ./.; + }; + + pythonAttr = "python3"; + in + { + devShells = forAllSystems (system: { + default = + let + pkgs = nixpkgs.legacyPackages.${system}; + python = pkgs.${pythonAttr}; + pythonEnv = python.withPackages (project.renderers.withPackages { inherit python; }); + in + pkgs.mkShell { packages = [ pythonEnv ]; }; + }); + + packages = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + python = pkgs.${pythonAttr}; + in + { + default = python.pkgs.buildPythonPackage (project.renderers.buildPythonPackage { inherit python; }); + } + ); + }; +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6179bdc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "hello-python" +version = "0.1.0" +description = "A minimal pyproject.toml" +authors = [ ] +requires-python = ">=3.13" +dependencies = [ "insightface", "opencv-python", "matplotlib" ] + + diff --git a/src/faceswap.py b/src/faceswap.py new file mode 100644 index 0000000..d04736c --- /dev/null +++ b/src/faceswap.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import cv2 +import random # TODO: seed! + +import insightface +from insightface.app import FaceAnalysis + +from insightface.model_zoo.inswapper import INSwapper +from insightface.app.common import Face + +SWAPPING_MODEL = 'inswapper_128.onnx' + +class Faceswapper: + def __init__(self): + self._app : FaceAnalysis = FaceAnalysis(name='buffalo_l') + self._model : INSwapper + self._source : cv2.typing.MatLike|None = None + self._source_faces : list[Face] = [] + self._template_faces : list[Face] = [] + + self._app.prepare(ctx_id=0, det_size=(640, 640)) + + def prepare_model(self) -> None: + self._model = insightface.model_zoo.get_model(SWAPPING_MODEL, download=True, download_zip=True) # Revealed type: "INSwapper" + + def set_source(self, path: str) -> None: + self._source = cv2.imread(path) + + def detect_source_faces(self): + self._source_faces = self._app.get(self._source) + + def add_template_faces(self, template_path: str): + self._template_faces += [self._app.get(cv2.imread(template_path))] + + def swap(self) -> cv2.typing.MatLike: + work_copy = self._source_faces.copy() + for i, face in enumerate(self._source_faces): + print(f"Replacing face {i+1}/{len(self._source_faces)}...", end="", flush=True); + imposed_face = random.choice(self._template_faces) + work_copy = self._model.get(work_copy, face, imposed_face, paste_back=True) + print(" done!") + + return work_copy + + @staticmethod + def readFile(path: str) -> cv2.typing.MatLike|None: + return cv2.imread(path) + + +def main(): + swapper = Faceswapper() + swapper.prepare_model() + + + +if __name__ == '__main__': + main()