├── .gitignore
├── LICENSE
├── README.md
├── example.png
├── lib
├── install.d.ts
├── install.js
├── main.d.ts
├── main.js
├── utils.d.ts
└── utils.js
├── models
└── config.yaml
├── package.json
├── pnpm-lock.yaml
├── src
├── install.ts
├── main.ts
└── utils.ts
├── test.mjs
├── tiny.png
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | venv
2 | node_modules
3 | models/*.pth
4 | *.jpg
5 | *.png
6 | !tiny.png
7 | !example.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 [fullname]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Transparent Background (npm)
2 |
3 | [](https://www.npmjs.com/package/transparent-background)
4 |
5 | `transparent-background` lets you **easily remove backgrounds** from images
6 |
7 | Powered by [InSPyReNet (ACCV 2022)](https://github.com/plemeri/InSPyReNet), thanks to [@plemeri](https://github.com/plemeri/) for creating [transparent-background](https://github.com/plemeri/transparent-background)
8 |
9 | - **Python** must be installed system-wide
10 | - Works on **Linux, macOS and Windows**
11 | - Python is already pre-installed on **macOS**
12 | - If failing on **Windows**, you might have to install **Python 3.8**
13 |
14 | ```bash
15 | pnpm install transparent-background
16 | yarn add transparent-background
17 | npm install transparent-background
18 | ```
19 |
20 | ## Example
21 |
22 | 
23 |
24 | ```ts
25 | import * as fs from "fs/promises";
26 | import { transparentBackground } from "transparent-background";
27 |
28 | // const fs = require("fs/promises");
29 | // const { transparentBackground } = require("transparent-background");
30 |
31 | (async () => {
32 | const input = await fs.readFile("test-input.png");
33 |
34 | const output = await transparentBackground(input, "png", {
35 | // uses a 1024x1024 model by default
36 | // enabling fast uses a 384x384 model instead
37 | fast: false,
38 | });
39 |
40 | await fs.writeFile("test-output.png", output);
41 | })();
42 | ```
43 |
--------------------------------------------------------------------------------
/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makinori/transparent-background-npm/833c4d05349401bfca56ce8c5e7167effd1b34d6/example.png
--------------------------------------------------------------------------------
/lib/install.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/lib/install.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | var desc = Object.getOwnPropertyDescriptor(m, k);
5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6 | desc = { enumerable: true, get: function() { return m[k]; } };
7 | }
8 | Object.defineProperty(o, k2, desc);
9 | }) : (function(o, m, k, k2) {
10 | if (k2 === undefined) k2 = k;
11 | o[k2] = m[k];
12 | }));
13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14 | Object.defineProperty(o, "default", { enumerable: true, value: v });
15 | }) : function(o, v) {
16 | o["default"] = v;
17 | });
18 | var __importStar = (this && this.__importStar) || function (mod) {
19 | if (mod && mod.__esModule) return mod;
20 | var result = {};
21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22 | __setModuleDefault(result, mod);
23 | return result;
24 | };
25 | var __importDefault = (this && this.__importDefault) || function (mod) {
26 | return (mod && mod.__esModule) ? mod : { "default": mod };
27 | };
28 | Object.defineProperty(exports, "__esModule", { value: true });
29 | const execa_1 = __importDefault(require("execa"));
30 | const fs = __importStar(require("fs/promises"));
31 | const path = __importStar(require("path"));
32 | const main_1 = require("./main");
33 | const utils_1 = require("./utils");
34 | (async () => {
35 | const systemPythonPath = await (0, utils_1.findSystemPython)();
36 | if (systemPythonPath == null) {
37 | throw new Error("Python not found. Please make sure its installed");
38 | }
39 | // make venv dir
40 | // this fails for some reasons
41 | // try {
42 | // await fs.rm(venvDir, { recursive: true, force: true });
43 | // } catch (error) {}
44 | await fs.mkdir(utils_1.venvDir, { recursive: true });
45 | // setup venv dir
46 | await (0, execa_1.default)(systemPythonPath, ["-m", "venv", utils_1.venvDir], {
47 | stdout: "inherit",
48 | stderr: "inherit",
49 | });
50 | // install package
51 | const venvPythonPath = path.resolve(utils_1.venvDir, utils_1.isWindows ? "Scripts/python.exe" : "bin/python");
52 | await (0, execa_1.default)(venvPythonPath, ["-m", "pip", "install", "-U", "transparent-background"], // 1.2.12
53 | {
54 | stdout: "inherit",
55 | stderr: "inherit",
56 | env: {
57 | VIRTUAL_ENV: utils_1.venvDir,
58 | },
59 | });
60 | // modify python file
61 | for (const libVersion of ["lib", "lib64", "Lib"]) {
62 | const venvLibDir = path.resolve(utils_1.venvDir, libVersion);
63 | if (!(await (0, utils_1.exists)(venvLibDir)))
64 | continue;
65 | const venvLibFiles = await fs.readdir(venvLibDir);
66 | for (const pythonVersion of venvLibFiles) {
67 | const removerPyPath = path.resolve(venvLibDir, pythonVersion, "site-packages/transparent_background/Remover.py");
68 | if (!(await (0, utils_1.exists)(removerPyPath)))
69 | continue;
70 | let removerPy = await fs.readFile(removerPyPath, "utf8");
71 | removerPy = removerPy.replaceAll(/home_dir = [^]+?\n/gi, "home_dir = os.getenv('MODELS_DIR')\n");
72 | await fs.writeFile(removerPyPath, removerPy);
73 | }
74 | }
75 | // lib/python3.11/site-packages/transparent_background/Remover.py
76 | // download models and test
77 | // if fails then installation will fail too
78 | // would be nice if we could move ~/.transparent-background inside here
79 | console.log("Downloading InSPyReNet models and testing them...");
80 | const tinyPng = await fs.readFile(path.resolve(__dirname, "../tiny.png"));
81 | await (0, main_1.transparentBackground)(tinyPng, "png");
82 | await (0, main_1.transparentBackground)(tinyPng, "png", { fast: true });
83 | })();
84 |
--------------------------------------------------------------------------------
/lib/main.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | export declare function transparentBackground(file: Buffer, fileExt: string, options?: {
3 | fast?: boolean;
4 | }): Promise;
5 |
--------------------------------------------------------------------------------
/lib/main.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | var desc = Object.getOwnPropertyDescriptor(m, k);
5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6 | desc = { enumerable: true, get: function() { return m[k]; } };
7 | }
8 | Object.defineProperty(o, k2, desc);
9 | }) : (function(o, m, k, k2) {
10 | if (k2 === undefined) k2 = k;
11 | o[k2] = m[k];
12 | }));
13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14 | Object.defineProperty(o, "default", { enumerable: true, value: v });
15 | }) : function(o, v) {
16 | o["default"] = v;
17 | });
18 | var __importStar = (this && this.__importStar) || function (mod) {
19 | if (mod && mod.__esModule) return mod;
20 | var result = {};
21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22 | __setModuleDefault(result, mod);
23 | return result;
24 | };
25 | Object.defineProperty(exports, "__esModule", { value: true });
26 | exports.transparentBackground = void 0;
27 | const execa = require("execa");
28 | const fs = __importStar(require("fs/promises"));
29 | const path = __importStar(require("path"));
30 | const tmp = __importStar(require("tmp-promise"));
31 | const utils_1 = require("./utils");
32 | async function transparentBackground(file, fileExt, options = {}) {
33 | if (!fileExt.startsWith("."))
34 | fileExt = "." + fileExt;
35 | const inputFile = await tmp.file({ postfix: fileExt });
36 | await fs.writeFile(inputFile.path, file);
37 | const outputDir = await tmp.dir({
38 | unsafeCleanup: true, // recursive
39 | });
40 | const { stdout, stderr } = await execa(utils_1.transparentBackgroundPath, [
41 | ...(options.fast ? ["-m", "fast"] : []),
42 | "--source",
43 | inputFile.path,
44 | "--dest",
45 | outputDir.path,
46 | ], {
47 | reject: false,
48 | env: {
49 | VIRTUAL_ENV: utils_1.venvDir,
50 | MODELS_DIR: utils_1.modelsDir,
51 | },
52 | });
53 | await inputFile.cleanup();
54 | const outputFilenames = await fs.readdir(outputDir.path);
55 | if (outputFilenames.length == 0) {
56 | await outputDir.cleanup();
57 | console.log(stdout);
58 | console.log(stderr);
59 | throw new Error("No output files");
60 | }
61 | const outputPath = path.resolve(outputDir.path, outputFilenames[0]);
62 | const outputBuffer = await fs.readFile(outputPath);
63 | outputDir.cleanup().catch(() => {
64 | fs.rm(outputDir.path, { recursive: true, force: true }).catch(() => {
65 | // dont wanna throw since the operation was successful
66 | console.log("Transparent background, failed to cleanup: " + outputDir.path);
67 | });
68 | });
69 | return outputBuffer;
70 | }
71 | exports.transparentBackground = transparentBackground;
72 |
--------------------------------------------------------------------------------
/lib/utils.d.ts:
--------------------------------------------------------------------------------
1 | export declare const isWindows: boolean;
2 | export declare const venvDir: string;
3 | export declare const modelsDir: string;
4 | export declare const transparentBackgroundPath: string;
5 | export declare function findSystemPython(): Promise;
6 | export declare function exists(filePath: string): Promise;
7 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 | if (k2 === undefined) k2 = k;
4 | var desc = Object.getOwnPropertyDescriptor(m, k);
5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6 | desc = { enumerable: true, get: function() { return m[k]; } };
7 | }
8 | Object.defineProperty(o, k2, desc);
9 | }) : (function(o, m, k, k2) {
10 | if (k2 === undefined) k2 = k;
11 | o[k2] = m[k];
12 | }));
13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14 | Object.defineProperty(o, "default", { enumerable: true, value: v });
15 | }) : function(o, v) {
16 | o["default"] = v;
17 | });
18 | var __importStar = (this && this.__importStar) || function (mod) {
19 | if (mod && mod.__esModule) return mod;
20 | var result = {};
21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22 | __setModuleDefault(result, mod);
23 | return result;
24 | };
25 | var __importDefault = (this && this.__importDefault) || function (mod) {
26 | return (mod && mod.__esModule) ? mod : { "default": mod };
27 | };
28 | Object.defineProperty(exports, "__esModule", { value: true });
29 | exports.exists = exports.findSystemPython = exports.transparentBackgroundPath = exports.modelsDir = exports.venvDir = exports.isWindows = void 0;
30 | const execa_1 = __importDefault(require("execa"));
31 | const fs = __importStar(require("fs/promises"));
32 | const os = __importStar(require("os"));
33 | const path = __importStar(require("path"));
34 | exports.isWindows = os.platform() == "win32";
35 | exports.venvDir = path.resolve(__dirname, "../venv");
36 | exports.modelsDir = path.resolve(__dirname, "../models");
37 | exports.transparentBackgroundPath = path.resolve(exports.venvDir, exports.isWindows
38 | ? "Scripts/transparent-background.exe"
39 | : "bin/transparent-background");
40 | async function findSystemPython() {
41 | const findPath = exports.isWindows ? "where" : "which";
42 | const pythonNames = ["python3", "python"];
43 | // windows has python3 in path but does microsoft store funny
44 | // which does stderr so lets try catch
45 | for (const pythonName of pythonNames) {
46 | try {
47 | const pythonPath = (await (0, execa_1.default)(findPath, [pythonName])).stdout.split("\n")[0]; // windows shows multiple lines
48 | if (pythonPath.includes("not found"))
49 | continue; // linux or mac
50 | if (pythonPath.includes("not find"))
51 | continue; // windows
52 | const pythonVersion = (await (0, execa_1.default)(pythonPath, ["--version"]))
53 | .stdout;
54 | if (!pythonVersion.includes("Python 3."))
55 | continue;
56 | return pythonPath;
57 | }
58 | catch (error) {
59 | continue;
60 | }
61 | }
62 | return null;
63 | }
64 | exports.findSystemPython = findSystemPython;
65 | async function exists(filePath) {
66 | try {
67 | await fs.stat(filePath);
68 | return true;
69 | }
70 | catch (error) {
71 | return false;
72 | }
73 | }
74 | exports.exists = exists;
75 |
--------------------------------------------------------------------------------
/models/config.yaml:
--------------------------------------------------------------------------------
1 | base:
2 | url: "https://drive.google.com/file/d/13oBl5MTVcWER3YU4fSxW3ATlVfueFQPY/view?usp=share_link"
3 | md5: "d692e3dd5fa1b9658949d452bebf1cda"
4 | ckpt_name: "ckpt_base.pth"
5 | http_proxy: NULL
6 | base_size: [1024, 1024]
7 |
8 |
9 | fast:
10 | url: "https://drive.google.com/file/d/1iRX-0MVbUjvAVns5MtVdng6CQlGOIo3m/view?usp=share_link"
11 | md5: "9efdbfbcc49b79ef0f7891c83d2fd52f"
12 | ckpt_name: "ckpt_fast.pth"
13 | http_proxy: NULL
14 | base_size: [384, 384]
15 |
16 | base-nightly:
17 | url: "https://drive.google.com/file/d/13YER0ri0RZkTdGQqWiwK795i39FrXNKL/view?usp=drive_link"
18 | md5: NULL
19 | ckpt_name: "ckpt_base_nightly.pth"
20 | http_proxy: NULL
21 | base_size: [1024, 1024]
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "transparent-background",
3 | "main": "lib/main.js",
4 | "version": "1.5.0",
5 | "license": "MIT",
6 | "repository": "https://github.com/makidoll/transparent-background-npm",
7 | "description": "transparent-background lets you easily remove backgrounds from images powered by InSPyReNet (ACCV 2022)",
8 | "scripts": {
9 | "build": "tsc",
10 | "install": "node lib/install.js"
11 | },
12 | "dependencies": {
13 | "execa": "^5.1.1",
14 | "tmp-promise": "^3.0.3"
15 | },
16 | "devDependencies": {
17 | "@types/node": "^20.11.21",
18 | "typescript": "^5.3.3"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | execa:
9 | specifier: ^5.1.1
10 | version: 5.1.1
11 | tmp-promise:
12 | specifier: ^3.0.3
13 | version: 3.0.3
14 |
15 | devDependencies:
16 | '@types/node':
17 | specifier: ^20.11.21
18 | version: 20.11.21
19 | typescript:
20 | specifier: ^5.3.3
21 | version: 5.3.3
22 |
23 | packages:
24 |
25 | /@isaacs/cliui@8.0.2:
26 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
27 | engines: {node: '>=12'}
28 | dependencies:
29 | string-width: 5.1.2
30 | string-width-cjs: /string-width@4.2.3
31 | strip-ansi: 7.1.0
32 | strip-ansi-cjs: /strip-ansi@6.0.1
33 | wrap-ansi: 8.1.0
34 | wrap-ansi-cjs: /wrap-ansi@7.0.0
35 | dev: false
36 |
37 | /@pkgjs/parseargs@0.11.0:
38 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
39 | engines: {node: '>=14'}
40 | requiresBuild: true
41 | dev: false
42 | optional: true
43 |
44 | /@types/node@20.11.21:
45 | resolution: {integrity: sha512-/ySDLGscFPNasfqStUuWWPfL78jompfIoVzLJPVVAHBh6rpG68+pI2Gk+fNLeI8/f1yPYL4s46EleVIc20F1Ow==}
46 | dependencies:
47 | undici-types: 5.26.5
48 | dev: true
49 |
50 | /ansi-regex@5.0.1:
51 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
52 | engines: {node: '>=8'}
53 | dev: false
54 |
55 | /ansi-regex@6.0.1:
56 | resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
57 | engines: {node: '>=12'}
58 | dev: false
59 |
60 | /ansi-styles@4.3.0:
61 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
62 | engines: {node: '>=8'}
63 | dependencies:
64 | color-convert: 2.0.1
65 | dev: false
66 |
67 | /ansi-styles@6.2.1:
68 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
69 | engines: {node: '>=12'}
70 | dev: false
71 |
72 | /balanced-match@1.0.2:
73 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
74 | dev: false
75 |
76 | /brace-expansion@2.0.1:
77 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
78 | dependencies:
79 | balanced-match: 1.0.2
80 | dev: false
81 |
82 | /color-convert@2.0.1:
83 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
84 | engines: {node: '>=7.0.0'}
85 | dependencies:
86 | color-name: 1.1.4
87 | dev: false
88 |
89 | /color-name@1.1.4:
90 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
91 | dev: false
92 |
93 | /cross-spawn@7.0.3:
94 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
95 | engines: {node: '>= 8'}
96 | dependencies:
97 | path-key: 3.1.1
98 | shebang-command: 2.0.0
99 | which: 2.0.2
100 | dev: false
101 |
102 | /eastasianwidth@0.2.0:
103 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
104 | dev: false
105 |
106 | /emoji-regex@8.0.0:
107 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
108 | dev: false
109 |
110 | /emoji-regex@9.2.2:
111 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
112 | dev: false
113 |
114 | /execa@5.1.1:
115 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
116 | engines: {node: '>=10'}
117 | dependencies:
118 | cross-spawn: 7.0.3
119 | get-stream: 6.0.1
120 | human-signals: 2.1.0
121 | is-stream: 2.0.1
122 | merge-stream: 2.0.0
123 | npm-run-path: 4.0.1
124 | onetime: 5.1.2
125 | signal-exit: 3.0.7
126 | strip-final-newline: 2.0.0
127 | dev: false
128 |
129 | /foreground-child@3.1.1:
130 | resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
131 | engines: {node: '>=14'}
132 | dependencies:
133 | cross-spawn: 7.0.3
134 | signal-exit: 4.1.0
135 | dev: false
136 |
137 | /get-stream@6.0.1:
138 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
139 | engines: {node: '>=10'}
140 | dev: false
141 |
142 | /glob@10.3.10:
143 | resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
144 | engines: {node: '>=16 || 14 >=14.17'}
145 | hasBin: true
146 | dependencies:
147 | foreground-child: 3.1.1
148 | jackspeak: 2.3.6
149 | minimatch: 9.0.3
150 | minipass: 7.0.4
151 | path-scurry: 1.10.1
152 | dev: false
153 |
154 | /human-signals@2.1.0:
155 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
156 | engines: {node: '>=10.17.0'}
157 | dev: false
158 |
159 | /is-fullwidth-code-point@3.0.0:
160 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
161 | engines: {node: '>=8'}
162 | dev: false
163 |
164 | /is-stream@2.0.1:
165 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
166 | engines: {node: '>=8'}
167 | dev: false
168 |
169 | /isexe@2.0.0:
170 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
171 | dev: false
172 |
173 | /jackspeak@2.3.6:
174 | resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
175 | engines: {node: '>=14'}
176 | dependencies:
177 | '@isaacs/cliui': 8.0.2
178 | optionalDependencies:
179 | '@pkgjs/parseargs': 0.11.0
180 | dev: false
181 |
182 | /lru-cache@10.2.0:
183 | resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
184 | engines: {node: 14 || >=16.14}
185 | dev: false
186 |
187 | /merge-stream@2.0.0:
188 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
189 | dev: false
190 |
191 | /mimic-fn@2.1.0:
192 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
193 | engines: {node: '>=6'}
194 | dev: false
195 |
196 | /minimatch@9.0.3:
197 | resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
198 | engines: {node: '>=16 || 14 >=14.17'}
199 | dependencies:
200 | brace-expansion: 2.0.1
201 | dev: false
202 |
203 | /minipass@7.0.4:
204 | resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
205 | engines: {node: '>=16 || 14 >=14.17'}
206 | dev: false
207 |
208 | /npm-run-path@4.0.1:
209 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
210 | engines: {node: '>=8'}
211 | dependencies:
212 | path-key: 3.1.1
213 | dev: false
214 |
215 | /onetime@5.1.2:
216 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
217 | engines: {node: '>=6'}
218 | dependencies:
219 | mimic-fn: 2.1.0
220 | dev: false
221 |
222 | /path-key@3.1.1:
223 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
224 | engines: {node: '>=8'}
225 | dev: false
226 |
227 | /path-scurry@1.10.1:
228 | resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
229 | engines: {node: '>=16 || 14 >=14.17'}
230 | dependencies:
231 | lru-cache: 10.2.0
232 | minipass: 7.0.4
233 | dev: false
234 |
235 | /rimraf@5.0.5:
236 | resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
237 | engines: {node: '>=14'}
238 | hasBin: true
239 | dependencies:
240 | glob: 10.3.10
241 | dev: false
242 |
243 | /shebang-command@2.0.0:
244 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
245 | engines: {node: '>=8'}
246 | dependencies:
247 | shebang-regex: 3.0.0
248 | dev: false
249 |
250 | /shebang-regex@3.0.0:
251 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
252 | engines: {node: '>=8'}
253 | dev: false
254 |
255 | /signal-exit@3.0.7:
256 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
257 | dev: false
258 |
259 | /signal-exit@4.1.0:
260 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
261 | engines: {node: '>=14'}
262 | dev: false
263 |
264 | /string-width@4.2.3:
265 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
266 | engines: {node: '>=8'}
267 | dependencies:
268 | emoji-regex: 8.0.0
269 | is-fullwidth-code-point: 3.0.0
270 | strip-ansi: 6.0.1
271 | dev: false
272 |
273 | /string-width@5.1.2:
274 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
275 | engines: {node: '>=12'}
276 | dependencies:
277 | eastasianwidth: 0.2.0
278 | emoji-regex: 9.2.2
279 | strip-ansi: 7.1.0
280 | dev: false
281 |
282 | /strip-ansi@6.0.1:
283 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
284 | engines: {node: '>=8'}
285 | dependencies:
286 | ansi-regex: 5.0.1
287 | dev: false
288 |
289 | /strip-ansi@7.1.0:
290 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
291 | engines: {node: '>=12'}
292 | dependencies:
293 | ansi-regex: 6.0.1
294 | dev: false
295 |
296 | /strip-final-newline@2.0.0:
297 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
298 | engines: {node: '>=6'}
299 | dev: false
300 |
301 | /tmp-promise@3.0.3:
302 | resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
303 | dependencies:
304 | tmp: 0.2.2
305 | dev: false
306 |
307 | /tmp@0.2.2:
308 | resolution: {integrity: sha512-ETcvHhaIc9J2MDEAH6N67j9bvBvu/3Gb764qaGhwtFvjtvhegqoqSpofgeyq1Sc24mW5pdyUDs9HP5j3ehkxRw==}
309 | engines: {node: '>=14'}
310 | dependencies:
311 | rimraf: 5.0.5
312 | dev: false
313 |
314 | /typescript@5.3.3:
315 | resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
316 | engines: {node: '>=14.17'}
317 | hasBin: true
318 | dev: true
319 |
320 | /undici-types@5.26.5:
321 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
322 | dev: true
323 |
324 | /which@2.0.2:
325 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
326 | engines: {node: '>= 8'}
327 | hasBin: true
328 | dependencies:
329 | isexe: 2.0.0
330 | dev: false
331 |
332 | /wrap-ansi@7.0.0:
333 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
334 | engines: {node: '>=10'}
335 | dependencies:
336 | ansi-styles: 4.3.0
337 | string-width: 4.2.3
338 | strip-ansi: 6.0.1
339 | dev: false
340 |
341 | /wrap-ansi@8.1.0:
342 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
343 | engines: {node: '>=12'}
344 | dependencies:
345 | ansi-styles: 6.2.1
346 | string-width: 5.1.2
347 | strip-ansi: 7.1.0
348 | dev: false
349 |
--------------------------------------------------------------------------------
/src/install.ts:
--------------------------------------------------------------------------------
1 | import execa from "execa";
2 | import * as fs from "fs/promises";
3 | import * as path from "path";
4 | import { transparentBackground } from "./main";
5 | import { exists, findSystemPython, isWindows, venvDir } from "./utils";
6 |
7 | (async () => {
8 | const systemPythonPath = await findSystemPython();
9 | if (systemPythonPath == null) {
10 | throw new Error("Python not found. Please make sure its installed");
11 | }
12 |
13 | // make venv dir
14 |
15 | // this fails for some reasons
16 | // try {
17 | // await fs.rm(venvDir, { recursive: true, force: true });
18 | // } catch (error) {}
19 |
20 | await fs.mkdir(venvDir, { recursive: true });
21 |
22 | // setup venv dir
23 |
24 | await execa(systemPythonPath, ["-m", "venv", venvDir], {
25 | stdout: "inherit",
26 | stderr: "inherit",
27 | });
28 |
29 | // install package
30 |
31 | const venvPythonPath = path.resolve(
32 | venvDir,
33 | isWindows ? "Scripts/python.exe" : "bin/python",
34 | );
35 |
36 | await execa(
37 | venvPythonPath,
38 | ["-m", "pip", "install", "-U", "transparent-background"], // 1.2.12
39 | {
40 | stdout: "inherit",
41 | stderr: "inherit",
42 | env: {
43 | VIRTUAL_ENV: venvDir,
44 | },
45 | },
46 | );
47 |
48 | // modify python file
49 |
50 | for (const libVersion of ["lib", "lib64", "Lib"]) {
51 | const venvLibDir = path.resolve(venvDir, libVersion);
52 | if (!(await exists(venvLibDir))) continue;
53 |
54 | const venvLibFiles = await fs.readdir(venvLibDir);
55 |
56 | for (const pythonVersion of venvLibFiles) {
57 | const removerPyPath = path.resolve(
58 | venvLibDir,
59 | pythonVersion,
60 | "site-packages/transparent_background/Remover.py",
61 | );
62 |
63 | if (!(await exists(removerPyPath))) continue;
64 |
65 | let removerPy = await fs.readFile(removerPyPath, "utf8");
66 |
67 | removerPy = removerPy.replaceAll(
68 | /home_dir = [^]+?\n/gi,
69 | "home_dir = os.getenv('MODELS_DIR')\n",
70 | );
71 |
72 | await fs.writeFile(removerPyPath, removerPy);
73 | }
74 | }
75 |
76 | // lib/python3.11/site-packages/transparent_background/Remover.py
77 |
78 | // download models and test
79 | // if fails then installation will fail too
80 | // would be nice if we could move ~/.transparent-background inside here
81 |
82 | console.log("Downloading InSPyReNet models and testing them...");
83 |
84 | const tinyPng = await fs.readFile(path.resolve(__dirname, "../tiny.png"));
85 |
86 | await transparentBackground(tinyPng, "png");
87 | await transparentBackground(tinyPng, "png", { fast: true });
88 | })();
89 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import execa = require("execa");
2 | import * as fs from "fs/promises";
3 | import * as path from "path";
4 | import * as tmp from "tmp-promise";
5 | import { modelsDir, transparentBackgroundPath, venvDir } from "./utils";
6 |
7 | export async function transparentBackground(
8 | file: Buffer,
9 | fileExt: string,
10 | options: { fast?: boolean } = {},
11 | ) {
12 | if (!fileExt.startsWith(".")) fileExt = "." + fileExt;
13 | const inputFile = await tmp.file({ postfix: fileExt });
14 |
15 | await fs.writeFile(inputFile.path, file);
16 |
17 | const outputDir = await tmp.dir({
18 | unsafeCleanup: true, // recursive
19 | });
20 |
21 | const { stdout, stderr } = await execa(
22 | transparentBackgroundPath,
23 | [
24 | ...(options.fast ? ["-m", "fast"] : []),
25 | "--source",
26 | inputFile.path,
27 | "--dest",
28 | outputDir.path,
29 | ],
30 | {
31 | reject: false,
32 | env: {
33 | VIRTUAL_ENV: venvDir,
34 | MODELS_DIR: modelsDir,
35 | },
36 | },
37 | );
38 |
39 | await inputFile.cleanup();
40 |
41 | const outputFilenames = await fs.readdir(outputDir.path);
42 | if (outputFilenames.length == 0) {
43 | await outputDir.cleanup();
44 |
45 | console.log(stdout);
46 | console.log(stderr);
47 |
48 | throw new Error("No output files");
49 | }
50 |
51 | const outputPath = path.resolve(outputDir.path, outputFilenames[0]);
52 | const outputBuffer = await fs.readFile(outputPath);
53 |
54 | outputDir.cleanup().catch(() => {
55 | fs.rm(outputDir.path, { recursive: true, force: true }).catch(() => {
56 | // dont wanna throw since the operation was successful
57 | console.log(
58 | "Transparent background, failed to cleanup: " + outputDir.path,
59 | );
60 | });
61 | });
62 |
63 | return outputBuffer;
64 | }
65 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import execa from "execa";
2 | import * as fs from "fs/promises";
3 | import * as os from "os";
4 | import * as path from "path";
5 |
6 | export const isWindows = os.platform() == "win32";
7 |
8 | export const venvDir = path.resolve(__dirname, "../venv");
9 | export const modelsDir = path.resolve(__dirname, "../models");
10 |
11 | export const transparentBackgroundPath = path.resolve(
12 | venvDir,
13 | isWindows
14 | ? "Scripts/transparent-background.exe"
15 | : "bin/transparent-background",
16 | );
17 |
18 | export async function findSystemPython() {
19 | const findPath = isWindows ? "where" : "which";
20 | const pythonNames = ["python3", "python"];
21 |
22 | // windows has python3 in path but does microsoft store funny
23 | // which does stderr so lets try catch
24 |
25 | for (const pythonName of pythonNames) {
26 | try {
27 | const pythonPath = (
28 | await execa(findPath, [pythonName])
29 | ).stdout.split("\n")[0]; // windows shows multiple lines
30 |
31 | if (pythonPath.includes("not found")) continue; // linux or mac
32 | if (pythonPath.includes("not find")) continue; // windows
33 |
34 | const pythonVersion = (await execa(pythonPath, ["--version"]))
35 | .stdout;
36 |
37 | if (!pythonVersion.includes("Python 3.")) continue;
38 |
39 | return pythonPath;
40 | } catch (error) {
41 | continue;
42 | }
43 | }
44 |
45 | return null;
46 | }
47 |
48 | export async function exists(filePath: string) {
49 | try {
50 | await fs.stat(filePath);
51 | return true;
52 | } catch (error) {
53 | return false;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/test.mjs:
--------------------------------------------------------------------------------
1 | import * as fs from "fs/promises";
2 | import { transparentBackground } from "./lib/main.js";
3 |
4 | // const fs = require("fs/promises");
5 | // const { transparentBackground } = require("transparent-background");
6 |
7 | (async () => {
8 | const input = await fs.readFile("test-input.jpg");
9 |
10 | const output = await transparentBackground(input, "jpg", {
11 | // uses a 1024x1024 model by default
12 | // enabling fast uses a 384x384 model instead
13 | fast: false,
14 | });
15 |
16 | await fs.writeFile("test-output.png", output);
17 | })();
18 |
--------------------------------------------------------------------------------
/tiny.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makinori/transparent-background-npm/833c4d05349401bfca56ce8c5e7167effd1b34d6/tiny.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["es2022"],
4 | "module": "commonjs",
5 | "target": "es2022",
6 | "outDir": "lib",
7 | "declaration": true,
8 | "esModuleInterop": true
9 | },
10 | "include": ["src/**/*"]
11 | }
12 |
--------------------------------------------------------------------------------