I have read in the docs, that when you do not specify x, y in the following method, that the image is sliced in to strips:
gm(image).crop(1280, 1)
However! It still only slices as if you write the method as:
gm(image).crop(1280, 1, 0, 0)
http://www.graphicsmagick.org/GraphicsMagick.html#details-crop
Has anyone had this problem, and is there a way to force slices of the given image?
Ok, so if anyone else finds this useful, this was my solution to making a function to iterate through an image and make slices:
/*
SLICE IMAGES
*/
// saved images as an array
var images = fs.readdirSync('captures');
// amount of saved images on disk
var imageCount = images.length;
// assume there are no images currently
var imageCounter = 0;
// create a random string to ID the slices
function randomStringGenerator(length, chars) {
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
return result;
}
// get images function to iterate over the images saved to disk
(function getImage() {
// use 'setTimeout' to get around memory issues
setTimeout(function () {
// if there are more images than have been currently iterated through
if (imageCount > imageCounter) {
// path to current image to be sliced
var image = 'captures/' + images[imageCounter];
// use the size method to get the image width and height, useful for images submitted on mobile etc.
gm(image).size(function(err, value){
// check for errors, TO DO: put this in 'if' statement
console.log('Error: ', err);
// get current image width
var imageWidth = value.width;
// get current image height
var imageHeight = value.height;
// start slicing on first pixel
var sliceCounter = 1;
//
(function getSlices() {
// use 'setTimeout' to get around memory issues
setTimeout(function() {
// if the image height is bigger than the current slice
if (imageHeight > sliceCounter) {
// apply the random string to the slice name, time not needed here as it is in the parent image file name
var randomString = randomStringGenerator(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
// crop image to the full width of current image and increments of 1 pixel
gm(image).crop(imageWidth, 1, sliceCounter, 0).write('slices/slice' + randomString + '.png', function (err) {
// check for errors, TO DO: put this in 'if' statement
console.log('Error: ', err);
// increase the slice counter, to affect the next slice
sliceCounter++;
// fire function recurssively, to help with memory
getSlices();
});
} else {
// if we have sliced the whole image, increase the 'imageCounter' to iterate over next image
imageCounter++;
// get next image
getImage();
}
}, 250);
})();
});
}
}, 250);
})();
Related
I have a for loop for checking multiple uploaded image's aspect ratio, after completing the loop i want to check ratio in if else condition to redirect user. Problem is the conditions are checked before loop finishes, I need the loop to be completed before checking conditions. I found out async whilst might be suitable here but i'm confused about the best approach for implemetation, can anyone give me workaround to perform the code sequentially.
//check image ratio
var validImageRatio = true;
for(i=0; i<req.files.propertyPhoto.length; i++){
var tempFile = req.files.propertyPhoto[i].tempFilePath.replace(/\\/g, '/');
var ratio;var width;var height;
var acceptedRatio = 3;
//get image ratio
sizeOf(tempFile, function (err, dimensions) {
width = dimensions.width;
height = dimensions.height;
ratio = width/height;
});
if (ratio < (acceptedRatio - 0.1) || ratio > (acceptedRatio + 0.1)) {
validImageRatio = false;
break;
}
}
//if ratio invalid, redirect
if (!validImageRatio) {
...
}
//if ratio valid, upload
else{
...
}
Since you're doing the check asynchronously, the synchronous code will run first. If you use async/await inside the for loop, it will block each iteration of the loop making it run slower. The approach you can go for is to use Promise.all to run the checks concurrently.
const promises = req.files.propertyPhoto.map(prop => new Promise(resolve => {
const tempFile = prop.tempFilePath.replace(/\\/g, '/');
const acceptedRatio = 3;
// get image ratio
sizeOf(tempFile, function (err, dimensions) {
const width = dimensions.width;
const height = dimensions.height;
const ratio = width / height;
if (ratio < (acceptedRatio - 0.1) || ratio > (acceptedRatio + 0.1)) {
return resolve(false);
}
resolve(true);
});
}));
const result = await Promise.all(promises);
if (result.some(r => r === false)) {
// if any of the ratio is invalid, redirect
} else {
// else upload
}
I'm guessing at what you mean, but a for-loop would complete before checking the conditions at the bottom except that you include a "break" statement. The break statement makes the for-loop stop executing and move on.
I am using the sharp nodejs library found here: https://github.com/lovell/sharp. I am trying to take several screenshots, and then piece the images together using the sharp library.
Here is my code. I am using puppeteer to take screenshots of the page, saving in memory as a binary file and combining those binary files together using sharp's composite() method.
let pagePath = 'path/to/file.png';
let maxScreenshotHeight = 2000;
// Loop over sections of the screen that are of size maxScreenshotHeight.
for (let ypos = 0; ypos < contentSize.height; ypos += maxScreenshotHeight) {
const height = Math.min(contentSize.height - ypos, maxScreenshotHeight);
let image = await page.screenshot({
encoding: 'binary',
clip: {
x: 0,
y: ypos,
width: contentSize.width,
height
}
});
composites.push({input: image, gravity: 'southeast'});
}
sharp()
.composite(composites)
.toFile(pagePath, function(err) {
if (err) {
console.log('fail');
return;
}
console.log('complete');
});
However, in the toFile callback, nothing ever gets logged. Console logging works, as I've added logs before and after the toFile statement, but it seems that this function call never completes. I want to create a png file that I can later download.
How can I merge these multiple screenshots and store them on the server for a later download? Am I using toFile incorrectly?
I'm using Jimp module of NodeJS to do some image transformation in my (big) nodejs script inside a big loop where my filename is dynamically generated.
When doing my jimp image transformation, the filename provided to write function parameter is already changed. Because the write is inside a callback (so a another thread?) and my loop is already continue to process another loop step.
Here is an example overview:
for (var i = 0; i < 10; ++i) {
var filename = 'test' + i + '.png';
//some script to generate my image : pupperteer screenshot of a webpage
Jimp.read(filename).then(function (image) {
image.greyscale().write(filename);
}).catch(function (err) {
console.error(err);
});
}
In this example, my script create the file test1.png in color, then I can view a test2.png appear which is a copy of test1.png but in greyscale... Then it overwrite by a new color image named test2.png.
So I'm wondering how to solve this?
It's perfectly fine to have it in multithread, so how to use a copy of "filename" string to use it in parameter of write function?
Regards
Alex
use let instead of var
let filename = 'test' + i + '.png';
You have a problem with asynchrony. I think that you could create a method and send filename as param. For example:
for (var i = 0; i < 10; ++i) {
let filename = 'test' + i + '.png';
//some script to generate my image : pupperteer screenshot of a webpage
_saveImg(filename);
}
function _saveImg(name) {
const filename = name;
Jimp.read(filename).then(function (image) {
image.greyscale().write(filename);
}).catch(function (err) {
console.error(err);
});
}
=)
I am making a code where users can upload a image. The image is converted with GraphicsMagick and uploaded to our cloud. But it will be best if non-transparent images was converted to JPG instead of PNG for transparent images. How can I check if the image contain a alpha channel in GraphicsMagick?
I am not sure you can achieve that using only GraphicsMagick, but it is possible in several other ways. For example with pngjs:
You can check PNG metadata:
const gm = require('gm');
const PNG = require('pngjs').PNG;
gm('/path/to/image')
.stream('png')
.pipe(new PNG({}))
.on('metadata', meta => {
if (meta.alpha) {
// image is transparent
} else {
// image is not transparent
}
});
Or iterate over pixels and decide if it's transparency valuable to you, or you can omit it:
...
.on('parsed', function() {
let isAlphaValuable = false;
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var idx = (this.width * y + x) << 2;
// this.data[idx] - red channel
// this.data[idx + 1] - green channel
// this.data[idx + 2] - blue channel
// this.data[idx + 3] - alpha channel
// if there is at least one pixel
// which transparent for more than 30%
// then transparency valuable to us
isAlphaValuable |= (1 - this.data[idx + 3] / 255) > 0.3;
}
}
if (isAlphaValuable) {
// keep transparency
} else {
// ignore transparency
}
});
You may also give imagemagick a try
Snippet is in TypeScript and makes use of BPromise.promisify for better readability.
Note that this works for PNG, JPEG the expected way (returning string true/false), but for GIFs it will give you a concatenated 'true'|'false' string (e.g. 'truetruefalse', and apply the alpha check per frame).
I also recommend applying .trim() to the result to get rid of potential useless whitespace returned by imagemagick v0.x. every now and then.
import * as imagemagick from 'imagemagick';
import * as BPromise from 'bluebird';
...
const opaqueAsync: any = BPromise.promisify(imagemagick.identify, {context: imagemagick});
const isOpaqueReturnValue: string = await opaqueAsync(['-format', '%[opaque]', picturePath]);
const isPicTransparent: boolean = 'false' === isOpaqueReturnValue.trim();
I need to check if a file is being uploaded to a FTP server. I have no control over the server side (I'd just love a file.temp rename), so my option (best guess to my knowledge of FTP) is to ask the file size or last modified after an interval. My problem is I would need this to be sync.
function isStillUploading(ftp, filePath) {
var startFileSize = -1;
var nextFileSize = -2;
ftp.size(filePath,
function sizeReturned(error, size) {
if (!error) startFileSize = size;
else startFileSize = -3;
});
// CPU-melting style delay.
var start = Date.now();
while (Date.now() - start < 500) { }
ftp.size(filePath,
function sizeReturned(error, size) {
if (!error) nextFileSize = size;
else nextFileSize = -4;
});
// This would be better, but I have no way of having
// root f wait for this and return a correct boolean.
//setTimeout(ftp.size(filePath,
// function sizeReturned(error, size) {
// if (!error) nextFileSize = size;
// else nextFileSize = -4;
// }),
// 500);
// TODO: add checks for -1 -2 -3 -4
console.log("File size change: " + startFileSize + ":" + nextFileSize);
return (startFileSize != nextFileSize);
}
Writing this in a callback style would still imply a loop somewhere to re-check the file's size or (IMO) nasty callback nesting I really don't like. As far as code readability goes, I think just making it sync would be so much easier.
Is there a simple way of doing this or should I re-write with callbacks and events?
Thank you for your help.