HTML5中Canvas如何调整大小(缩小)图像高质量?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (5705)

使用html5 canvas元素在我的浏览器中调整图像大小。事实证明,质量非常低。我发现这一点:缩放<canvas>时禁用插值,但它无助于提高质量。

下面是我的css和js代码以及用Photoshop调出的图像,并在画布API中缩放。

在浏览器中缩放图像时,为了获得最佳质量,我需要做些什么?

注意:想将大图像缩小为小图像,在画布中修改颜色并将画布的结果发送到服务器。

CSS:

canvas, img {
    image-rendering: optimizeQuality;
    image-rendering: -moz-crisp-edges;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: optimize-contrast;
    -ms-interpolation-mode: nearest-neighbor;
}

JS:

var $img = $('<img>');
var $originalCanvas = $('<canvas>');
$img.load(function() {


   var originalContext = $originalCanvas[0].getContext('2d');   
   originalContext.imageSmoothingEnabled = false;
   originalContext.webkitImageSmoothingEnabled = false;
   originalContext.mozImageSmoothingEnabled = false;
   originalContext.drawImage(this, 0, 0, 379, 500);
});

该图片使用photoshop重新调整大小:

在画布上调整图像大小:

编辑:

我试图按照以下几个步骤进行多级缩减:

在HTML5画布和 Html5画布drawImage中调整图像大小:如何应用抗锯齿

这是我用过的功能:

function resizeCanvasImage(img, canvas, maxWidth, maxHeight) {
    var imgWidth = img.width, 
        imgHeight = img.height;

    var ratio = 1, ratio1 = 1, ratio2 = 1;
    ratio1 = maxWidth / imgWidth;
    ratio2 = maxHeight / imgHeight;

    // Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
    if (ratio1 < ratio2) {
        ratio = ratio1;
    }
    else {
        ratio = ratio2;
    }

    var canvasContext = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");
    var canvasCopy2 = document.createElement("canvas");
    var copyContext2 = canvasCopy2.getContext("2d");
    canvasCopy.width = imgWidth;
    canvasCopy.height = imgHeight;  
    copyContext.drawImage(img, 0, 0);

    // init
    canvasCopy2.width = imgWidth;
    canvasCopy2.height = imgHeight;        
    copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);


    var rounds = 2;
    var roundRatio = ratio * rounds;
    for (var i = 1; i <= rounds; i++) {
        console.log("Step: "+i);

        // tmp
        canvasCopy.width = imgWidth * roundRatio / i;
        canvasCopy.height = imgHeight * roundRatio / i;

        copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);

        // copy back
        canvasCopy2.width = imgWidth * roundRatio / i;
        canvasCopy2.height = imgHeight * roundRatio / i;
        copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);

    } // end for


    // copy back to canvas
    canvas.width = imgWidth * roundRatio / rounds;
    canvas.height = imgHeight * roundRatio / rounds;
    canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);


}

如果我使用2步缩小尺寸,结果如下:

如果我使用3步缩小尺寸,结果如下:

如果我使用4步缩小尺寸,结果如下:

如果我使用20步缩小尺寸,结果如下:

注意:事实证明,从1步到2步,图像质量有很大改善,但添加到该过程的步骤越多,图像变得越模糊。

有没有办法解决添加的步骤越多,图像越模糊的问题?

编辑2013-10-04:尝试了GameAlchemist的算法。这是与Photoshop相比的结果。

PhotoShop图片:

GameAlchemist的算法:

提问于
用户回答回答于

更新: 2.0版(更快,网络工作者+可转移对象)

/**
 * Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
 * 
 * @param {HtmlElement} canvas
 * @param {int} width
 * @param {int} height
 * @param {boolean} resize_canvas if true, canvas will be resized. Optional.
 */
function resample_single(canvas, width, height, resize_canvas) {
    var width_source = canvas.width;
    var height_source = canvas.height;
    width = Math.round(width);
    height = Math.round(height);

    var ratio_w = width_source / width;
    var ratio_h = height_source / height;
    var ratio_w_half = Math.ceil(ratio_w / 2);
    var ratio_h_half = Math.ceil(ratio_h / 2);

    var ctx = canvas.getContext("2d");
    var img = ctx.getImageData(0, 0, width_source, height_source);
    var img2 = ctx.createImageData(width, height);
    var data = img.data;
    var data2 = img2.data;

    for (var j = 0; j < height; j++) {
        for (var i = 0; i < width; i++) {
            var x2 = (i + j * width) * 4;
            var weight = 0;
            var weights = 0;
            var weights_alpha = 0;
            var gx_r = 0;
            var gx_g = 0;
            var gx_b = 0;
            var gx_a = 0;
            var center_y = (j + 0.5) * ratio_h;
            var yy_start = Math.floor(j * ratio_h);
            var yy_stop = Math.ceil((j + 1) * ratio_h);
            for (var yy = yy_start; yy < yy_stop; yy++) {
                var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
                var center_x = (i + 0.5) * ratio_w;
                var w0 = dy * dy; //pre-calc part of w
                var xx_start = Math.floor(i * ratio_w);
                var xx_stop = Math.ceil((i + 1) * ratio_w);
                for (var xx = xx_start; xx < xx_stop; xx++) {
                    var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
                    var w = Math.sqrt(w0 + dx * dx);
                    if (w >= 1) {
                        //pixel too far
                        continue;
                    }
                    //hermite filter
                    weight = 2 * w * w * w - 3 * w * w + 1;
                    var pos_x = 4 * (xx + yy * width_source);
                    //alpha
                    gx_a += weight * data[pos_x + 3];
                    weights_alpha += weight;
                    //colors
                    if (data[pos_x + 3] < 255)
                        weight = weight * data[pos_x + 3] / 250;
                    gx_r += weight * data[pos_x];
                    gx_g += weight * data[pos_x + 1];
                    gx_b += weight * data[pos_x + 2];
                    weights += weight;
                }
            }
            data2[x2] = gx_r / weights;
            data2[x2 + 1] = gx_g / weights;
            data2[x2 + 2] = gx_b / weights;
            data2[x2 + 3] = gx_a / weights_alpha;
        }
    }
    //clear and resize canvas
    if (resize_canvas === true) {
        canvas.width = width;
        canvas.height = height;
    } else {
        ctx.clearRect(0, 0, width_source, height_source);
    }

    //draw
    ctx.putImageData(img2, 0, 0);
}

热门问答

关于notebook文件导出cos桶报错的疑问?

腾讯智能钛AI开发者

腾讯云 · 智能钛产品团队 (已认证)

腾讯智能钛产品团队官方运营账号。分享产品最新动态,第一时间解答用户疑问。
推荐
这边看您从个人 COS 路径中导入数据到 Notebook ,那么您需要参照以下代码进行鉴权和数据导入。 获取密钥: import os import requests cred_url = os.environ["QCLOUD_CONTAINER_INSTANCE_CREDEN...... 展开详请

iOS实时音视频的SDK和Demo有没有Objective-C版本?

腾讯视频云-ZacharyTXLiteAVSDK技术支持
推荐
下载专业版和企业版的压缩包里面带的官方demo是Objective-C的,下载地址:https://cloud.tencent.com/document/product/647/32689 image.png ... 展开详请

请问小游戏联机对战引擎能在cocoscreater3d中运行吗?

无聊至极互联网重度用户
推荐
您好!非常感谢您的反馈,您的问题我们已经收到,小游戏联机对战引擎能兼容 crearor 3d。 因为 cocoscreator 近期会发布新版本,开发者可以非常便捷的在 crearor2d 的 ide 里面直接开通 mgobe 服务并上传实时服务器代码,所以我们更推荐您使用 cr...... 展开详请

腾讯会议API创建用户UserId 参数设置无效,没有主持人?

推荐
您好!非常感谢您的反馈,您的问题我们已经收到。 api创建的会议,没有主持人权限,请确认请求头中是否有带X-TC-Registered参数。 X-TC-Registered没带是不能获取到主持人身份的。 若解决中还存在其它问题,欢迎继续在社区反馈或【提交工单】,我们收到后会尽快处...... 展开详请

Android腾讯移动推送快速接入无法运行,tpnsplugin什么时候能修正此问题?

您好,给您造成不便深表歉意,TPNS 团队已经确认此问题,并将在近期排期更新插件以适配新版本 gradle 插件,还请您耐心等待。 目前您可以考虑降级 gradle 插件版本来继续使用 TPNS 快速集成插件,或参考 Android 接入指南:https://cloud.tenc...... 展开详请

windows(C++)版本的TRTC能不能支持xp?

腾讯视频云-ZacharyTXLiteAVSDK技术支持
推荐
不支持,最低支持WIndows 7,集成文档:https://cloud.tencent.com/document/product/647/32178 image.png ... 展开详请

所属标签

扫码关注云+社区

领取腾讯云代金券