Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions @types/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare type Options = {
filename?: string;
folderPath?: string;
width: number;
height?: number;
};
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# node-image-slice

Command-line utility that slices an input image into segments according to specified width and height, and outputs those segments into a folder.
Command-line utility that slices input images into segments according to specified width and height, and outputs those segments into a folder.

## Install

Expand All @@ -16,18 +16,17 @@ Command-line utility that slices an input image into segments according to speci

## Usage

`node slice.cjs <filename> <width> [height]`
`node slice.cjs`

- If `filename` does not include an extension, `.png` will be guessed
- If `height` is not specified, `width` will be used as `height`

## Supported formats
```
-f, --filename Input image filename [string]
-i, --folderPath Input folder [string]
-w, --width Output image width [number] [required]
-h, --height Output image height [number]
```

- jpeg
- png
- bmp
- tiff
- gif
- If `filename` does not include an extension, `.png`, `.gif`, `.jpg` and `.jpeg` will be guessed
- If `height` is not specified, `width` will be used as `height`

## Background

Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-image-slice",
"version": "1.0.0",
"version": "2.0.0",
"description": "Slices an input image into segments according to specified width and height",
"repository": {
"type": "git",
Expand All @@ -11,19 +11,22 @@
"type": "commonjs",
"main": "slice.cjs",
"files": [
"slice.cjs"
"slice.cjs",
"utils"
],
"scripts": {
"build": "tsc && npm run renameCjs && npm run prettier",
"renameCjs": "node -e \"require('fs').renameSync('slice.js', 'slice.cjs')\"",
"prettier": "prettier \"./**/*.{ts,d.ts,cjs,md,json}\" --write"
},
"devDependencies": {
"@types/yargs": "^17.0.32",
"prettier": "^3.1.1",
"tsc": "^2.0.4",
"typescript": "~5.2.2"
},
"dependencies": {
"jimp": "~0.22.10"
"jimp": "~0.22.10",
"yargs": "^17.7.2"
}
}
109 changes: 52 additions & 57 deletions slice.cjs
Original file line number Diff line number Diff line change
@@ -1,61 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Jimp = require("jimp");
const fs = require("fs");
const path = require("path");
const outputFolder = "output";
function sliceImage(filename, width, height) {
Jimp.read(filename, (err, image) => {
if (err) {
// Try again by appending '.png' to the filename
const pngFilename = `${filename}.png`;
Jimp.read(pngFilename, (pngErr, pngImage) => {
if (pngErr) {
console.error("Error reading the image:", pngErr);
return;
}
continueSlicing(pngImage, width, height, filename);
});
} else {
continueSlicing(image, width, height, filename);
}
});
}
function continueSlicing(image, width, height, inputFilename) {
// If height is not specified, use width as height
height = height || width;
const imageWidth = image.getWidth();
const imageHeight = image.getHeight();
// Calculate the number of slices in both dimensions
const horizontalSlices = Math.ceil(imageWidth / width);
const verticalSlices = Math.ceil(imageHeight / height);
// Create a folder for output if it doesn't exist
if (!fs.existsSync(outputFolder)) {
fs.mkdirSync(outputFolder);
}
// Slice the image and save each segment
for (let y = 0; y < verticalSlices; y++) {
for (let x = 0; x < horizontalSlices; x++) {
const startX = x * width;
const startY = y * height;
const sliceWidth = Math.min(width, imageWidth - startX);
const sliceHeight = Math.min(height, imageHeight - startY);
const slice = image.clone().crop(startX, startY, sliceWidth, sliceHeight);
// Incorporate the input filename into the output filename
const baseFilename = path.basename(
inputFilename,
path.extname(inputFilename),
);
const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`;
slice.write(outputFilename);
console.log(`Slice saved: ${outputFilename}`);
}
const yargs = require("yargs");
const os = require("os");
const processImage_1 = require("./utils/processImage");
const processPath_1 = require("./utils/processPath");
// Parse command line arguments
const options = yargs
.option("f", {
alias: "filename",
describe: "Input image filename",
type: "string",
})
.option("i", {
alias: "folderPath",
describe: "Input folder",
type: "string",
})
.option("w", {
alias: "width",
describe: "Output image width",
type: "number",
demandOption: true,
coerce: (value) => {
if (value < 1) {
throw new Error("width should not be lower than 1");
}
return Math.round(value);
},
})
.option("h", {
alias: "height",
describe: "Output image height",
type: "number",
coerce: (value) => {
if (value !== undefined && value < 1) {
throw new Error("height should not be lower than 1");
}
return Math.round(value);
},
}).argv;
if (options.filename) {
// Process a single image
const { filename, width, height } = options;
(0, processImage_1.sliceImage)(filename, width, height);
} else if (options.folderPath) {
// Process all images in a folder, splitting the task into threads
let numCores = 2;
try {
numCores = os.cpus().length;
} catch (err) {
console.error(err);
}
}
// Get input from the command line arguments
const [filename, width, height] = process.argv.slice(2);
if (!filename || !width) {
console.log("Usage: node slice.cjs <filename> <width> [height]");
} else {
sliceImage(filename, parseInt(width), parseInt(height));
numCores = Math.max(numCores - 1, 1);
(0, processPath_1.processPath)(options.folderPath, options, numCores);
}
130 changes: 55 additions & 75 deletions src/slice.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,57 @@
import * as Jimp from "jimp";
import * as fs from "fs";
import * as path from "path";

const outputFolder = "output";

function sliceImage(filename: string, width: number, height?: number): void {
Jimp.read(filename, (err, image) => {
if (err) {
// Try again by appending '.png' to the filename
const pngFilename = `${filename}.png`;
Jimp.read(pngFilename, (pngErr, pngImage) => {
if (pngErr) {
console.error("Error reading the image:", pngErr);
return;
}
continueSlicing(pngImage, width, height, filename);
});
} else {
continueSlicing(image, width, height, filename);
}
});
}

function continueSlicing(
image: Jimp,
width: number,
height: number | undefined,
inputFilename: string,
): void {
// If height is not specified, use width as height
height = height || width;

const imageWidth = image.getWidth();
const imageHeight = image.getHeight();

// Calculate the number of slices in both dimensions
const horizontalSlices = Math.ceil(imageWidth / width);
const verticalSlices = Math.ceil(imageHeight / height);

// Create a folder for output if it doesn't exist
if (!fs.existsSync(outputFolder)) {
fs.mkdirSync(outputFolder);
}

// Slice the image and save each segment
for (let y = 0; y < verticalSlices; y++) {
for (let x = 0; x < horizontalSlices; x++) {
const startX = x * width;
const startY = y * height;

const sliceWidth = Math.min(width, imageWidth - startX);
const sliceHeight = Math.min(height, imageHeight - startY);

const slice = image.clone().crop(startX, startY, sliceWidth, sliceHeight);

// Incorporate the input filename into the output filename
const baseFilename = path.basename(
inputFilename,
path.extname(inputFilename),
);
const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`;

slice.write(outputFilename);
console.log(`Slice saved: ${outputFilename}`);
}
import * as yargs from "yargs";
import * as os from "os";

import { sliceImage } from "./utils/processImage";
import { processPath } from "./utils/processPath";

// Parse command line arguments
const options = yargs
.option("f", {
alias: "filename",
describe: "Input image filename",
type: "string",
})
.option("i", {
alias: "folderPath",
describe: "Input folder",
type: "string",
})
.option("w", {
alias: "width",
describe: "Output image width",
type: "number",
demandOption: true,
coerce: (value) => {
if (value < 1) {
throw new Error("width should not be lower than 1");
}
return Math.round(value);
},
})
.option("h", {
alias: "height",
describe: "Output image height",
type: "number",
coerce: (value) => {
if (value !== undefined && value < 1) {
throw new Error("height should not be lower than 1");
}
return Math.round(value);
},
}).argv as unknown as Options;

if (options.filename) {
// Process a single image
const { filename, width, height } = options;
sliceImage(filename, width, height);
} else if (options.folderPath) {
// Process all images in a folder, splitting the task into threads
let numCores = 2;
try {
numCores = os.cpus().length;
} catch (err) {
console.error(err);
}
}

// Get input from the command line arguments
const [filename, width, height] = process.argv.slice(2);

if (!filename || !width) {
console.log("Usage: node slice.cjs <filename> <width> [height]");
} else {
sliceImage(filename, parseInt(width), parseInt(height));
numCores = Math.max(numCores - 1, 1);
processPath(options.folderPath, options, numCores);
}
Loading