Resize the image to get a specific max. file size

I'm looking for a function that takes an image and that makes sure the target file size is less than a given size, but as large as possible.

The reason is to create vcard max. photo size - 224 KB.
(Apple or vcard limitation: https://support.apple.com/en-us/HT202158 )

I would like to have a function like this:

function (imageBase64, maxFileSize)
{
    // Check if image is smaller
    // If yes, return the old one.
    // If not, reduce image size proportional to fit in maxFileSize
    // return newImage
}

      

Do you have any hints how to solve it?

I would rather do this client side in js. But if this is not possible then I accept server-side php solutions as well.

+3


source to share


1 answer


One possible solution is to change the jpeg quality setting.

Instead of repeating many quality settings, I created an estimate of image sizes for quality settings.

I get the file size of the image with the best quality, and then use the ratings sizes to guess what the quality setting is under the file size. To get as close as possible, I move the quality settings up and down and check the file size several times.

There is a size threshold that, if a quality parameter is found with a file size below the required size and above the required size threshold percentage, that quality setting is used.

If no threshold is found, it will use the best quality setting that passed.

If he's having trouble finding a good setting, he'll just give a very low setting.



If the quality value is zero, it didn't work.

Required functions

// this function converts a image to a canvas image
function image2Canvas(image){
    var canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    canvas.ctx = canvas.getContext("2d");
    canvas.ctx.drawImage(image,0,0);
    return canvas;
}
// warning try to limit calls to this function as it can cause problems on some systems
// as they try to keep up with GC
// This function gets the file size by counting the number of Base64 characters and 
// calculating the number of bytes encoded.
function getImageFileSize(image,quality){  // image must be a canvas
    return Math.floor(image.toDataURL("image/jpeg",quality).length * (3/4));
}

function qualityForSize(image,fileSize){
    // These are approximations only
    // and are the result of using a test image and finding the file size
    // at quality setting 1 to 0.1 in 0.1 steps
    const scalingFactors = [
        5638850/5638850,
        1706816/5638850,
        1257233/5638850,
        844268/5638850,
        685253/5638850,
        531014/5638850,
        474293/5638850,
        363686/5638850,
        243578/5638850,
        121475/5638850,
        0, // this is added to catch the stuff ups.
    ]
    var size = getImageFileSize(image,1); // get file size at best quality;
    if(size <= fileSize){ // best quality is a pass
        return 1;
    }
    // using size make a guess at the quality setting
    var index = 0;
    while(size * scalingFactors[index] > fileSize){ index += 1 }
    if(index === 10){  // Could not find a quality setting 
        return 0; // this is bad and should not be used as a quality setting
    }
    var sizeUpper = size * scalingFactors[index-1];  // get estimated size at upper quality
    var sizeLower = size * scalingFactors[index];  // get estimated size at lower quality
    // estimate quality via linear interpolation
    var quality = (1-(index/10)) + ((fileSize - sizeLower) / (sizeUpper-sizeLower)) * 0.1;
    var qualityStep = 0.02; // the change in quality (this value gets smaller each try)
    var numberTrys = 3;  //  number of trys to get as close as posible to the file size
    var passThreshold = 0.90; // be within 90% of desiered file size
    var passQualities = []; // array of quality settings that are under file size
    while(numberTrys--){
         var newSize = getImageFileSize(image,quality); // get the file size for quality guess
         if(newSize <= fileSize && newSize/fileSize > passThreshold ){ // does it pass?
             return quality;  // yes return quality
         }
         if(newSize > fileSize){  // file size too big 
            quality -= qualityStep;  // try lower quality
            qualityStep /= 2;        // reduce the quality step for next try
         }else{
            passQualities.push(quality);  // save this quality incase nothing get within the pass threashold
            quality += qualityStep;  // step the quality up.
            qualityStep /= 2;        // reduce the size of the next quality step     
         }
    }
    // could not find a quality setting so get the best we did find
    if(passQualities.length > 0){ //check we did get a pass
           passQualities.sort();  // sort to get best pass quality
           return passQualities.pop(); // return best quality that passed
    }
    // still no good result so just default to next 0.1 step down
    return 1-((index+1)/10);
}

      

How to use

// testImage is the image to set quality of
// the image must be loaded
var imgC = image2Canvas(testImage); // convert image to a canvas
var qualitySetting = qualityForSize(imgC,244000);   // find the image quality to be under file size 244000
// convert to data URL
var dataURL = imgC.toDataURL("image/jpeg",qualitySetting);
// the saved file will be under 244000

      

+3


source







All Articles