How to Compress an Image with JavaScript
Image compressing on the browser is a useful operation. It can improve the uploading performance and save the storage. In this article, we are going to talk about how to compress an image with JavaScript using the Dynamsoft Document Viewer SDK.
We can compress an image by changing the following aspects:
- Resolution
- Color depth
- Image format
- Image quality
New HTML Page
Create a new page with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Image Compression</title>
<style>
</style>
</head>
<body>
</body>
<script>
</script>
</html>
Add Dynamsoft Document Viewer
Dynamsoft Document Viewer provides a set of viewers for document scanning. It supports a bunch of image processing methods and image formats we can use to compress an image. Let’s add it to the page to use it.
-
Include the CSS and JavaScript files of Dynamsoft Document Viewer
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/ddv.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/ddv.css">
-
Set the license. You can apply for a license here.
let license = "LICENSE-KEY"; Dynamsoft.DDV.Core.license = license;
-
Init the library.
Dynamsoft.DDV.Core.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/engine";// Lead to a folder containing the distributed WASM files await Dynamsoft.DDV.Core.init();
-
Create a doc instance for loading and saving image files.
let doc; const docManager = Dynamsoft.DDV.documentManager; doc = docManager.createDocument();
-
Load an image from blob waiting for processing.
await doc.loadSource(imgBlob);
Reduce the Resolution
Reducing the resolution can achieve siginificant size reduction.
Code:
function resize(newWidth, newHeight){
const pageUid = doc.pages[0];
const pageData = await doc.getPageData(pageUid)
const imageProcess = Dynamsoft.DDV.Experiments.get("ImageProcess");
const result = await imageProcess.process({type:3, data:pageData.display.data}, {type:13/*resize*/, params:{newWidth:newWidth, newHeight:newHeight, mode:2}});
const newImage = new Blob([result.output],{type:result.outputContentType});
await doc.updatePage(pageUid, newImage, {});
}
Example:
The 512x512 lena.png
file is reduced from 482KB to 110KB by changing the resolution to 256x256.
Reduce the Color Depth
We can convert the image to a 8-bit grayscale image or a 1-bit black and white image to reduce the size.
Code:
async function performColorConversion(type){
const pageUid = doc.pages[0];
const pageData = await doc.getPageData(pageUid)
const imageProcess = Dynamsoft.DDV.Experiments.get("ImageProcess");
const result = await imageProcess.process({type:3, data:pageData.display.data}, {type: type/*1: blackAndWhite 2: gray*/, params:{saveInk:false, level:1}});
const newImage = new Blob([result.output],{type:result.outputContentType});
await doc.updatePage(pageUid, newImage, {});
}
Example:
The lena.png
file can be reduced from a 482KB file to a 200KB grayscale file or a 11KB black and white file.
Reduce the Image Quality
Some image formats like JPEG support setting the image quality. It will degrade the image quality to reduce the size. If the quality is not set too small, the image will not have perceptible changes but the size can be reduced.
Code:
let blob = await doc.saveToJpeg(0,{quality: 50});
Example:
The lena.jpg
file can be reduced from a 125KB file to a 10KB file using the 0.1 quality.
Use an Image Format with a Higher Compression Rate
There are various image formats or compression algorithms we can use. The latter four are mainly used for compressing PDF files.
- JPEG - a lossy algorithm for images
- PNG - a raster-graphics file format that supports lossless data compression
- WebP - a modern image format that provides superior lossless and lossy compression for images on the web
- JP2000 - a more modern alternative to JPEG
- CCITT-4 (FAX4) - used for monochrome images
- JBIG2 - an alternative to CCITT compression for monochrome images
- LZW - used for compressing text as well as images
Code:
let blob;
let quality = 1.0;
let imageFormat = "image/webp";
const isPDF = imageFormat.indexOf("pdf") != -1;
if (imageFormat === "image/png") {
blob = await doc.saveToPng(0,{});
}else if (imageFormat === "image/jpeg") {
blob = await doc.saveToJpeg(0,{quality: quality*100});
console.log({quality: quality*100});
}else if (imageFormat === "image/webp") {
let pngBlob = await doc.saveToPng(0,{});
blob = await convertBlobToWebP(pngBlob,quality);
}else if (isPDF) {
blob = await doc.saveToPdf([0],{quality: quality*100,compression:imageFormat});
}
function convertBlobToWebP(blob,quality){
return new Promise((resolve, reject) => {
const url = URL.createObjectURL(blob);
let tmpImg = document.createElement("img");
tmpImg.onload = function(){
let width = tmpImg.naturalWidth;
let height = tmpImg.naturalHeight;
let context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
context.drawImage(tmpImg, 0, 0, tmpImg.naturalWidth, tmpImg.naturalHeight, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob)=>{
resolve(blob);
},"image/webp",quality)
}
tmpImg.src = url;
})
}
Compression Test
Here is a table of the test result compressing the images with different parameters of this image: link.
Color Conversion | Image Format | Quality | Size | Time Elapsed | Compression Rate |
---|---|---|---|---|---|
No conversion | image/png | 1 | 9506.63KB | 1763ms | 440.60% |
No conversion | image/jpeg | 0.1 | 272.04KB | 580ms | 12.61% |
No conversion | image/jpeg | 0.2 | 326.59KB | 613ms | 15.14% |
No conversion | image/jpeg | 0.3 | 375.83KB | 581ms | 17.42% |
No conversion | image/jpeg | 0.4 | 428.28KB | 598ms | 19.85% |
No conversion | image/jpeg | 0.5 | 498.50KB | 618ms | 23.10% |
No conversion | image/jpeg | 0.6 | 576.01KB | 608ms | 26.70% |
No conversion | image/jpeg | 0.7 | 750.01KB | 590ms | 34.76% |
No conversion | image/jpeg | 0.8 | 1069.88KB | 590ms | 49.59% |
No conversion | image/jpeg | 0.9 | 1827.61KB | 604ms | 84.70% |
No conversion | image/jpeg | 1 | 4049.13KB | 635ms | 187.66% |
No conversion | image/webp | 0.1 | 103.14KB | 2869ms | 4.78% |
No conversion | image/webp | 0.2 | 119.90KB | 2804ms | 5.56% |
No conversion | image/webp | 0.3 | 137.97KB | 2912ms | 6.39% |
No conversion | image/webp | 0.4 | 162.52KB | 2816ms | 7.53% |
No conversion | image/webp | 0.5 | 189.29KB | 2951ms | 8.77% |
No conversion | image/webp | 0.6 | 218.95KB | 2886ms | 10.15% |
No conversion | image/webp | 0.7 | 245.80KB | 2852ms | 11.39% |
No conversion | image/webp | 0.8 | 355.04KB | 2946ms | 16.45% |
No conversion | image/webp | 0.9 | 817.46KB | 3029ms | 37.89% |
No conversion | image/webp | 1 | 5964.65KB | 4900ms | 276.44% |
No conversion | pdf/jbig2 | 1 | 1070.78KB | 684ms | 49.63% |
No conversion | pdf/fax4 | 1 | 1070.78KB | 613ms | 49.63% |
No conversion | pdf/jp2000 | 0.1 | 1.11KB | 2398ms | 0.05% |
No conversion | pdf/jp2000 | 0.2 | 1.51KB | 2612ms | 0.07% |
No conversion | pdf/jp2000 | 0.3 | 35.31KB | 2599ms | 1.64% |
No conversion | pdf/jp2000 | 0.4 | 239.69KB | 2550ms | 11.11% |
No conversion | pdf/jp2000 | 0.5 | 1976.63KB | 2592ms | 91.61% |
No conversion | pdf/jp2000 | 0.6 | 3915.27KB | 2570ms | 181.46% |
No conversion | pdf/jp2000 | 0.7 | 4577.19KB | 2568ms | 212.14% |
No conversion | pdf/jp2000 | 0.8 | 4782.33KB | 2551ms | 221.65% |
No conversion | pdf/jp2000 | 0.9 | 4836.04KB | 2547ms | 224.13% |
No conversion | pdf/jp2000 | 1 | 4843.24KB | 2550ms | 224.47% |
No conversion | pdf/lzw | 1 | 9142.31KB | 1243ms | 423.72% |
Grayscale | image/png | 1 | 6001.92KB | 573ms | 278.17% |
Grayscale | image/jpeg | 0.1 | 224.03KB | 268ms | 10.38% |
Grayscale | image/jpeg | 0.2 | 277.29KB | 262ms | 12.85% |
Grayscale | image/jpeg | 0.3 | 327.86KB | 258ms | 15.20% |
Grayscale | image/jpeg | 0.4 | 388.02KB | 266ms | 17.98% |
Grayscale | image/jpeg | 0.5 | 407.98KB | 260ms | 18.91% |
Grayscale | image/jpeg | 0.6 | 777.40KB | 272ms | 36.03% |
Grayscale | image/jpeg | 0.7 | 951.35KB | 284ms | 44.09% |
Grayscale | image/jpeg | 0.8 | 1012.78KB | 278ms | 46.94% |
Grayscale | image/jpeg | 0.9 | 1406.44KB | 283ms | 65.18% |
Grayscale | image/jpeg | 1 | 3213.54KB | 313ms | 148.94% |
Grayscale | image/webp | 0.1 | 98.02KB | 1419ms | 4.54% |
Grayscale | image/webp | 0.2 | 117.39KB | 1473ms | 5.44% |
Grayscale | image/webp | 0.3 | 136.96KB | 1439ms | 6.35% |
Grayscale | image/webp | 0.4 | 162.58KB | 1466ms | 7.53% |
Grayscale | image/webp | 0.5 | 191.67KB | 1483ms | 8.88% |
Grayscale | image/webp | 0.6 | 225.83KB | 1539ms | 10.47% |
Grayscale | image/webp | 0.7 | 257.49KB | 1504ms | 11.93% |
Grayscale | image/webp | 0.8 | 391.60KB | 1611ms | 18.15% |
Grayscale | image/webp | 0.9 | 856.50KB | 1717ms | 39.70% |
Grayscale | image/webp | 1 | 5990.37KB | 3264ms | 277.63% |
Grayscale | pdf/jbig2 | 1 | 1013.68KB | 418ms | 46.98% |
Grayscale | pdf/fax4 | 1 | 1013.68KB | 335ms | 46.98% |
Grayscale | pdf/jp2000 | 0.1 | 1.09KB | 1429ms | 0.05% |
Grayscale | pdf/jp2000 | 0.2 | 1.49KB | 1428ms | 0.07% |
Grayscale | pdf/jp2000 | 0.3 | 35.25KB | 1425ms | 1.63% |
Grayscale | pdf/jp2000 | 0.4 | 247.83KB | 1447ms | 11.49% |
Grayscale | pdf/jp2000 | 0.5 | 1759.55KB | 1459ms | 81.55% |
Grayscale | pdf/jp2000 | 0.6 | 3337.76KB | 1499ms | 154.69% |
Grayscale | pdf/jp2000 | 0.7 | 3816.45KB | 1461ms | 176.88% |
Grayscale | pdf/jp2000 | 0.8 | 3878.43KB | 1488ms | 179.75% |
Grayscale | pdf/jp2000 | 0.9 | 3884.98KB | 1466ms | 180.06% |
Grayscale | pdf/jp2000 | 1 | 3885.36KB | 1460ms | 180.07% |
Grayscale | pdf/lzw | 1 | 5301.60KB | 476ms | 245.71% |
Black & white | image/png | 1 | 130.18KB | 115ms | 6.03% |
Black & white | image/jpeg | 0.1 | 363.82KB | 374ms | 16.86% |
Black & white | image/jpeg | 0.2 | 472.41KB | 190ms | 21.89% |
Black & white | image/jpeg | 0.3 | 570.84KB | 192ms | 26.46% |
Black & white | image/jpeg | 0.4 | 642.30KB | 191ms | 29.77% |
Black & white | image/jpeg | 0.5 | 706.09KB | 192ms | 32.72% |
Black & white | image/jpeg | 0.6 | 771.68KB | 195ms | 35.76% |
Black & white | image/jpeg | 0.7 | 855.62KB | 198ms | 39.66% |
Black & white | image/jpeg | 0.8 | 974.84KB | 194ms | 45.18% |
Black & white | image/jpeg | 0.9 | 1222.58KB | 195ms | 56.66% |
Black & white | image/jpeg | 1 | 2258.45KB | 215ms | 104.67% |
Black & white | image/webp | 0.1 | 290.87KB | 1037ms | 13.48% |
Black & white | image/webp | 0.2 | 327.36KB | 1057ms | 15.17% |
Black & white | image/webp | 0.3 | 352.60KB | 832ms | 16.34% |
Black & white | image/webp | 0.4 | 378.89KB | 856ms | 17.56% |
Black & white | image/webp | 0.5 | 396.79KB | 875ms | 18.39% |
Black & white | image/webp | 0.6 | 416.35KB | 869ms | 19.30% |
Black & white | image/webp | 0.7 | 432.61KB | 858ms | 20.05% |
Black & white | image/webp | 0.8 | 469.18KB | 868ms | 21.74% |
Black & white | image/webp | 0.9 | 543.33KB | 889ms | 25.18% |
Black & white | image/webp | 1 | 107.07KB | 259ms | 4.96% |
Black & white | pdf/jbig2 | 1 | 58.00KB | 198ms | 2.69% |
Black & white | pdf/fax4 | 1 | 127.27KB | 124ms | 5.90% |
Black & white | pdf/jp2000 | 0.1 | 58.00KB | 185ms | 2.69% |
Black & white | pdf/jp2000 | 0.2 | 58.00KB | 189ms | 2.69% |
Black & white | pdf/jp2000 | 0.3 | 58.00KB | 175ms | 2.69% |
Black & white | pdf/jp2000 | 0.4 | 58.00KB | 185ms | 2.69% |
Black & white | pdf/jp2000 | 0.5 | 58.00KB | 176ms | 2.69% |
Black & white | pdf/jp2000 | 0.6 | 58.00KB | 175ms | 2.69% |
Black & white | pdf/jp2000 | 0.7 | 58.00KB | 177ms | 2.69% |
Black & white | pdf/jp2000 | 0.8 | 58.00KB | 189ms | 2.69% |
Black & white | pdf/jp2000 | 0.9 | 58.00KB | 177ms | 2.69% |
Black & white | pdf/jp2000 | 1 | 58.00KB | 184ms | 2.69% |
Black & white | pdf/lzw | 1 | 127.27KB | 110ms | 5.90% |
Source Code
You can find all the code and an online demo in the following repo: