调整图像大小以获得特定的最大值。文件大小

时间:2017-05-16 18:00:07

标签: javascript html5-canvas

我正在寻找一个拍摄图像的功能,它确保目标文件的大小小于给定的大小,但要尽可能大。

原因是最大限度地创建了一个vcard。照片大小为224KB (Apple或vcard限制:https://support.apple.com/en-us/HT202158

我希望有这样的功能:

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
}

你有任何提示如何解决它?

我更愿意在js中做这个客户端。 但如果不可能,我也接受服务器端的php解决方案。

1 个答案:

答案 0 :(得分:3)

一种可能的解决方案是更改jpeg质量设置。

我没有迭代许多质量设置,而是根据质量设置创建了图像尺寸估算值。

我以最佳质量获取图像的文件大小,然后使用估计大小将质量设置猜测为文件大小。为了尽可能接近,我上下移动质量设置并检查文件大小几次。

如果发现质量设置的文件大小低于所需大小且高于所需大小的阈值百分比,则会有一个阈值大小,然后使用该质量设置。

如果未找到阈值,它将使用通过的最佳质量设置。

如果在查找质量设置时遇到问题,那么设置的设置就会非常低。

如果质量设置为零,则表示失败。

需要的功能

// 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);
}

如何使用

// 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