Fix image orientation in Jimp - node.js

I am using Jimp (https://www.npmjs.com/package/jimp) library to crop the image.
Crop is working fine but I only have an issue with image orientation.
Sometimes, user uploaded rotated images and its result rotated cropped images.
I went through with https://www.npmjs.com/package/jimp documentation but couldn't find anything related to this.
Here are couple of links I went through but didn't helped:
https://justmarkup.com/articles/2019-10-21-image-orientation/
Accessing JPEG EXIF rotation data in JavaScript on the client side
Please help

So, long story short: jimp correctly reads images rotated via exif orientation property and rearranges the pixels as if the exif/orientation property didn't exist, but then also stores the old exif property instead of resetting it to 1 as it should for it to be displayed properly on every device.
The simplest solution I was able to implement was using exif-auto-rotate to rotate the image pixels and reset the exif property on the frontend before uploading the (base64 encoded) image to the backend:
import Rotator from 'exif-auto-rotate';
// ...
const [file] = e.target.files;
const image = await Rotator.createRotatedImageAsync(file, "base64")
.catch(err => {
if (err === "Image is NOT have a exif code" || err === "Image is NOT JPEG") {
// just return base64 encoded image if image is not jpeg or contains no exif orientation property
return toBase64(file)
}
// reject if other error
return Promise.reject(err)
});
If you need to do this on the backend then you are probably better off using jpeg-autorotate with buffers as suggested here:
const fileIn = fs.readFileSync('input.jpg')
const jo = require('jpeg-autorotate')
const {buffer} = await jo.rotate(fileIn, {quality: 30})
const image = await Jimp.read(buffer)
More info on browser-based exif orientation issues:
EXIF Orientation Handling Is a Ghetto

just change the jimp version to
"jimp": "0.8.5",

Related

Nuxt convert image to webp before sending to AWS S3

How can I convert images to WEBP format before sending them to AWS S3 in my NUXT app?
I have a photo upload on my website, I would like to convert the images from the file input to WEBP format before uploading to the Amazon web service. Unlike NodeJS where I can import sharp and use it to convert the images to WEBP format, it is not the case here as I get an error like the like below
Failed to compile with 4 errors friendly-errors 01:16:19
These dependencies were not found: friendly-errors 01:16:19
friendly-errors 01:16:19
* child_process in ./node_modules/detect-libc/lib/detect-libc.js, ./node_modules/sharp/lib/libvips.js friendly-errors 01:16:19
* fs in ./node_modules/detect-libc/lib/detect-libc.js, ./node_modules/sharp/lib/libvips.js friendly-errors 01:16:19
friendly-errors 01:16:19
To install them, you can run: npm install --save child_process fs
and I would like to convert the images like in my code below
drop(e) {
e.preventDefault();
e.stopPropagation();
e.target.classList.remove('solid');
const files = e.dataTransfer.files;
this.handleFiles(files);
},
onFilePicked(e) {
e.preventDefault();
e.stopPropagation();
const files = e.target.files;
this.handleFiles(files);
},
saveToBackend(file, result) {
// compress image
// save to aws
},
readFiles(file) {
const reader = new FileReader();
reader.readAsDataURL(file)
reader.onload = () => {
const uploading = this.uploading
const result = reader.result
uploading.push(result)
this.uploading = uploading
// upload to aws
this.saveToBackend(file, result)
}
},
handleFiles(files) {
const Files = Array.from(files);
Files.forEach(file => {
// check if image is a valid image
if (file.type.includes('image')) {
// display the image
return this.readFiles(file)
}
return
});
console.log(Files, "loaded files");
},
and for the sharp plugin
import vue from "vue"
import sharp from "sharp"
vue.use(sharp)
please how can I compress the images?
you could use the packages imagemin and imagemin-webp as answered here: Convert Images to webp with Node
As I've explained you in your previous question, you cannot use a Node.js plugin into a client side app, especially when this one is already running and especially if you're hosting it as target: static on some Vercel or alike platform.
On top of this, image processing is pretty heavy in terms of required processing. So, having an external server that is doing this as a middleware is the best idea. You'll be able to make a load balancer, allocate auto-scaling, prevent a client side timeout and allow for a simpler way to debug things (maybe even more benefits actually).
You could maybe even do it on a serverless function, if you will not be bothered to much with slower cold starts.
TLDR:
simple and efficient solution, put a Node.js server in-between your Nuxt and your S3 bucket
more affordable one but more complex, call a serverless function for this (not even sure that this will be performant)
wait for Nuxt3 with Nitro, and make some shenigans with a local serviceWorker and Cloudflare workers, in edge-rendering (not even sure that this is the most adapted way of handling your issue neither)
maybe try to see for a not so expensive online service to handle the middleware for you
At the end, Image or Video is heavy and expensive to process. And doing those things require quite some knowledge too!
Eventually, I was able to solve my problem without using any package, and what I did was simply convert the image to a canvas and then I converted the image to WEBP format. Below is my solution.
convertImage(file) {
return new Promise((resolve) => {
// convert image
let src = URL.createObjectURL(file)
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d')
let userImage = new Image();
userImage.src = src
userImage.onload = function() {
canvas.width = userImage.width;
canvas.height = userImage.height;
ctx.drawImage(userImage, 0, 0);
let webpImage = canvas.toDataURL("image/webp");
return resolve(webpImage);
}
});
},
so, the function above first receives a file which is the image you want to convert from file input, then it converts the image into a canvas then converts the canvas back into an image, but this time you specify the format of the image you want to convert it into.
Since in my case, I wanted a webp image, I set canvas.toDataURL("image/webp") and by default, the quality of the WEBP image will be the same quality as the image that is received. if you want to reduce the quality to lower quality, the canvas.toDataURL("image/webp", 1) takes another argument which is a number between 0-1, 1 for the highest quality, and 0 lowest quality. you could set 0.5 for medium quality too, or whatever you want. You could also set other file formats you want through the first argument like canvas.toDataURL('image/jpeg', 1.0)-- for jpeg format or canvas.toDataURL('image/png', 1.0)--for png.
sources
the small channel where I found my solution - Where I found my solution
developer.mozilla.org explanation - more on the CanvasElement.toDataURL()

How to convert a remote webp image to a png image to load it into node-canvas

Usually I import png images into Canvas by using
const image = Canvas.LoadImage('url.png')
const canvas = Canvas.createCanvas(256,256)
const ctx = canvas.getContext('2d')
ctx.drawImage(image,256,256)
but when i try to import a webp image, I get an error saying that webp isn't supported. On research into the Issues of node-canvas I found this issue, The importing issue appears to be solved but I dont understand how to import the webp images now.
I tried using the Image, ImageFromBuffer(await fetch(url).buffer()) from the fix in the issue but both give errors.
I solved it by using the library Sharp.
First get the file as a buffer
// axios for remote images- maybe fs for local images?
const imageResponse = await axios.get(url, {
responseType: 'arraybuffer',
});
Convert the file to png using sharp:
const img = await sharp(imageResponse.data).toFormat('png').toBuffer();
Then you can use loadImage
const file = await loadImage(img).then((image) => {
ctx.drawImage(
image,
256,
256
);
return { buffer: canvas.toBuffer(), mimetype: `image/png` };
});

Getting EXIF data from images using ImageMagick inside AWS Lambda

I'm trying to extract EXIF data from images using ImageMagick inside AWS Lambda but I can't find the way to do it.
I have a piece of code to resize the image, it's working fine but I want to add the part to extract EXIF data.
Here is what I have right now to resize images:
var im = require("gm").subClass({imageMagick: true});
var operation = im(image.buffer).autoOrient().resize(width, height, '^');
operation.toBuffer(image.imageType, function(err, buffer) {
if (err) {
//do something with the error
} else {
//do something with the image
}
});
Any idea how to extract the metadata from the image ?
Thanks.
C.C.

resizing image while saving it's exif orientation with node-gm

I'm writing a nodeJS 5.3.0 application using gm (http://aheckmann.github.io/gm/)
I know that it uses the GraphicsMagicK library.
the problem is that I'm having is that after I resize an image, it loses it's exif format. the code samples actually shows that the exif format is lost.
for example:
var fs = require('fs')
, gm = require('gm').subClass({imageMagick: true});
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
in this example they say that exif profile data is removed.
I know that I can get the orientation of an image before resizing using:
gm('path/tp/my/img.jpg').orientation(function(err,value){
var orientation = value;
});
the question is..
can I preserve exif data when resizing ? and If not.. can I set exif orientation data after resizing ?
thanks
More specifically in the following code, only noProfile() function remove EXIF, so if you remove it you can preserve EXIF data
// resize and remove EXIF profile data
gm('/path/to/my/img.jpg')
.resize(240, 240)
.noProfile()
.write('/path/to/resize.png', function (err) {
if (!err) console.log('done');
});
Otherwise you can check the gm doc here

NodeJS: Rotating JPEG with GM not working

I'm writing an API endpoint to accept images and send them to my S3 server. Before that, however, it has to orient the images correctly so that not display wrong when in image tags. Since JPEGs use exif data to determine image rotation and the img tag does not refer to the exif data for orientation information, my images appear sideways on my website, but right side up on other platforms. Here is my code for rotating the image:
if (file.mimetype == 'image/jpeg'){
var exifData = Parser.create(uploadedFile).parse();
if (exifData.tags.Orientation == 6 || exifData.tags.Orientation == 8){
var degrees = exifData.tags.Orientation == 6 ? 90 : 270;
gm(file.path).rotate('black', degrees).write(file.path, function(err){
if (err)
return cb1(err);
finish();
});
}else
finish();
}else
finish();
finish() is the function that sends the file off to S3. Why is it that my image is not rotating? I have it set to overwrite the existing file on rotate. There are no errors being returned, as the entire call would fail.

Resources