canvas-plus icon indicating copy to clipboard operation
canvas-plus copied to clipboard

get dpi and set quality by specific size

Open akwatra opened this issue 7 years ago • 1 comments

Hi Joseph, its seems good, yet i have to try this. can you implement or suggest to achive with html 5 canvas api

-- how to get the image dpi eg: 300 pixels or 96 so we can . -- how can we resize image by specify image weight size., as uploaded image of x size and we want to reduce image at spefic jpeg quality 90 or 30 , but still image weight is higher than requirement, so if we can get what quality param we can get specific image weight size

ex: upload image : 5mb set weight : 200kb get quality param: 10 %

resize( jpeg_quality ) get resize image of what weight we wanted

akwatra avatar Jun 04 '18 09:06 akwatra

Hello there!

To get the image DPI, you can access the EXIF data via the getEXIF() method call, and examine the XResolution and YResolution properties. Example:

{
	"Make": "Apple",
	"Model": "iPhone 7",
	"Orientation": 1,
	"XResolution": 72,
	"YResolution": 72,
	"ResolutionUnit": 2,
	"Software": "10.3.1",
	"DateTime": "2017:04:29 16:36:23",
	...
}

This example image is 72x72 API. Note that only some images provide this EXIF data, so have a default fallback (72 DPI is typical). Please call this function immediately after loading the image, as most transforms wipe the EXIF data (because they clone and copy/paste the canvas).

For saving images based on a target file size, there is no provided way using the node-canvas library (on the server) or HTML5 Canvas API (on the client). They only allow you to specify the JPEG quality as a percentage number (1 - 100).

The only way I can think to accomplish this is to do a brute-force method. Meaning, save at 50%, measure size, increase or decrease by 25%, measure size, then 12.5%, etc. then either add half or subtract half, and repeat until you get close to your target size. To prevent an infinite loop, only allow 8 iterations or so. Quick example:

var fs = require('fs');
var async = require('async');
var CanvasPlus = require('canvas-plus');
var canvas = new CanvasPlus();

canvas.load('test.jpg', function(err) {
	if (err) throw err;
	
	var target_size = 50 * 1024; // 50KB target
	var idx = 0;
	var max_iterations = 8;
	var current_quality = 50;
	var current_size = 0;
	var current_buffer = null;
	var adjust_by = 25;
	
	async.doWhilst(
		function(callback) {
			// test a save, record size
			canvas.write({format: "jpeg", quality: Math.floor(current_quality)}, function(err, buf) {
				idx++;
				current_buffer = buf;
				console.log( current_quality + '% Quality: ' + buf.length + ' bytes' );
				
				if (buf.length < target_size) {
					current_quality += adjust_by;
					adjust_by /= 2;
				}
				else if (buf.length > target_size) {
					current_quality -= adjust_by;
					adjust_by /= 2;
				}
				else {
					idx = max_iterations;
				}
				
				if (current_quality < 1) current_quality = 1;
				callback();
			});
		},
		function() {
			return( idx < max_iterations );
		},
		function(err) {
			console.log("\nFinal image: " + current_buffer.length + " bytes (" + canvas.get('quality') + "% quality)");
			fs.writeFileSync( 'myimage.jpg', current_buffer );
		}
	); // doWhilst

});

This works on my test image (target: 50K):

50% Quality: 44309 bytes
75% Quality: 67193 bytes
62.5% Quality: 52631 bytes
56.25% Quality: 47946 bytes
59.375% Quality: 50268 bytes
60.9375% Quality: 50980 bytes
61.71875% Quality: 52006 bytes
61.328125% Quality: 52006 bytes

Final image: 52006 bytes (61% quality)

Good luck!

jhuckaby avatar Jun 06 '18 02:06 jhuckaby