調整HTML5畫布中圖像的大小

本文翻譯自:Resizing an image in an HTML5 canvas

I'm trying to create a thumbnail image on the client side using javascript and a canvas element, but when I shrink the image down, it looks terrible. 我正在嘗試使用javascript和canvas元素在客戶端創建縮略圖,但是當我縮小圖像時,它看起來很糟糕。 It looks as if it was downsized in photoshop with the resampling set to 'Nearest Neighbor' instead of Bicubic. 看起來好像是在Photoshop中縮小了尺寸,將重採樣設置爲“最近的鄰居”而不是Bicubic。 I know its possible to get this to look right, because this site can do it just fine using a canvas as well. 我知道有可能使它看起來正確,因爲該站點也可以使用畫布來完成它。 I've tried using the same code they do as shown in the "[Source]" link, but it still looks terrible. 我嘗試使用與[[Source]]鏈接中所示相同的代碼,但是它看起來仍然很糟糕。 Is there something I'm missing, some setting that needs to be set or something? 是否有我所缺少的東西,需要設置的設置或其他東西?

EDIT: 編輯:

I'm trying to resize a jpg. 我正在嘗試調整jpg的大小。 I have tried resizing the same jpg on the linked site and in photoshop, and it looks fine when downsized. 我嘗試在鏈接的網站和photoshop中調整相同jpg的大小,縮小尺寸後看起來不錯。

Here is the relevant code: 以下是相關代碼:

reader.onloadend = function(e)
{
    var img = new Image();
    var ctx = canvas.getContext("2d");
    var canvasCopy = document.createElement("canvas");
    var copyContext = canvasCopy.getContext("2d");

    img.onload = function()
    {
        var ratio = 1;

        if(img.width > maxWidth)
            ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
            ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
    };

    img.src = reader.result;
}

EDIT2: 編輯2:

Seems I was mistaken, the linked website wasn't doing any better of a job of downsizing the image. 似乎我弄錯了,鏈接的網站在縮小圖像尺寸方面做得更好。 I tried the other methods suggested and none of them look any better. 我嘗試了建議的其他方法,但沒有一個看起來更好。 This is what the different methods resulted in: 這是不同方法導致的結果:

Photoshop: Photoshop:

替代文字

Canvas: 帆布:

替代文字

Image with image-rendering: optimizeQuality set and scaled with width/height: 具有圖像渲染的圖像:optimizeQuality設置並隨寬度/高度縮放:

替代文字

Image with image-rendering: optimizeQuality set and scaled with -moz-transform: 具有圖像渲染的圖像:optimizeQuality設置並使用-moz-transform縮放:

替代文字

Canvas resize on pixastic: 畫布在像素上調整大小:

替代文字

I guess this means firefox isn't using bicubic sampling like its supposed to. 我猜這意味着Firefox並未像預期的那樣使用三次三次採樣。 I'll just have to wait until they actually add it. 我只需要等待,直到他們實際添加它。

EDIT3: 編輯3:

Original Image 原始圖片


#1樓

參考:https://stackoom.com/question/9fII/調整HTML-畫布中圖像的大小


#2樓

This is a javascript function adapted from @Telanor's code. 這是從@Telanor的代碼改編而成的javascript函數。 When passing a image base64 as first argument to the function, it returns the base64 of the resized image. 將圖像base64作爲第一個參數傳遞給函數時,它將返回調整後大小的圖像的base64。 maxWidth and maxHeight are optional. maxWidth和maxHeight是可選的。

function thumbnail(base64, maxWidth, maxHeight) {

  // Max size for thumbnail
  if(typeof(maxWidth) === 'undefined') var maxWidth = 500;
  if(typeof(maxHeight) === 'undefined') var maxHeight = 500;

  // Create and initialize two canvas
  var canvas = document.createElement("canvas");
  var ctx = canvas.getContext("2d");
  var canvasCopy = document.createElement("canvas");
  var copyContext = canvasCopy.getContext("2d");

  // Create original image
  var img = new Image();
  img.src = base64;

  // Determine new ratio based on max size
  var ratio = 1;
  if(img.width > maxWidth)
    ratio = maxWidth / img.width;
  else if(img.height > maxHeight)
    ratio = maxHeight / img.height;

  // Draw original image in second canvas
  canvasCopy.width = img.width;
  canvasCopy.height = img.height;
  copyContext.drawImage(img, 0, 0);

  // Copy and resize second canvas to first canvas
  canvas.width = img.width * ratio;
  canvas.height = img.height * ratio;
  ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

  return canvas.toDataURL();

}

#3樓

Thanks @syockit for an awesome answer. 感謝@syockit提供了一個很棒的答案。 however, I had to reformat a little as follows to make it work. 但是,我必須重新格式化以下內容才能使其正常運行。 Perhaps due to DOM scanning issues: 可能是由於DOM掃描問題:

$(document).ready(function () {

$('img').on("load", clickA);
function clickA() {
    var img = this;
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 50, 3);
    document.body.appendChild(canvas);
}

function thumbnailer(elem, img, sx, lobes) {
    this.canvas = elem;
    elem.width = img.width;
    elem.height = img.height;
    elem.style.display = "none";
    this.ctx = elem.getContext("2d");
    this.ctx.drawImage(img, 0, 0);
    this.img = img;
    this.src = this.ctx.getImageData(0, 0, img.width, img.height);
    this.dest = {
        width: sx,
        height: Math.round(img.height * sx / img.width)
    };
    this.dest.data = new Array(this.dest.width * this.dest.height * 3);
    this.lanczos = lanczosCreate(lobes);
    this.ratio = img.width / sx;
    this.rcp_ratio = 2 / this.ratio;
    this.range2 = Math.ceil(this.ratio * lobes / 2);
    this.cacheLanc = {};
    this.center = {};
    this.icenter = {};
    setTimeout(process1, 0, this, 0);
}

//returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
    return function (x) {
        if (x > lobes)
            return 0;
        x *= Math.PI;
        if (Math.abs(x) < 1e-16)
            return 1
        var xx = x / lobes;
        return Math.sin(x) * Math.sin(xx) / x / xx;
    }
}

process1 = function (self, u) {
    self.center.x = (u + 0.5) * self.ratio;
    self.icenter.x = Math.floor(self.center.x);
    for (var v = 0; v < self.dest.height; v++) {
        self.center.y = (v + 0.5) * self.ratio;
        self.icenter.y = Math.floor(self.center.y);
        var a, r, g, b;
        a = r = g = b = 0;
        for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
            if (i < 0 || i >= self.src.width)
                continue;
            var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
            if (!self.cacheLanc[f_x])
                self.cacheLanc[f_x] = {};
            for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
                if (j < 0 || j >= self.src.height)
                    continue;
                var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
                if (self.cacheLanc[f_x][f_y] == undefined)
                    self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
                weight = self.cacheLanc[f_x][f_y];
                if (weight > 0) {
                    var idx = (j * self.src.width + i) * 4;
                    a += weight;
                    r += weight * self.src.data[idx];
                    g += weight * self.src.data[idx + 1];
                    b += weight * self.src.data[idx + 2];
                }
            }
        }
        var idx = (v * self.dest.width + u) * 3;
        self.dest.data[idx] = r / a;
        self.dest.data[idx + 1] = g / a;
        self.dest.data[idx + 2] = b / a;
    }

    if (++u < self.dest.width)
        setTimeout(process1, 0, self, u);
    else
        setTimeout(process2, 0, self);
};

process2 = function (self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0);
    self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
    var idx, idx2;
    for (var i = 0; i < self.dest.width; i++) {
        for (var j = 0; j < self.dest.height; j++) {
            idx = (j * self.dest.width + i) * 3;
            idx2 = (j * self.dest.width + i) * 4;
            self.src.data[idx2] = self.dest.data[idx];
            self.src.data[idx2 + 1] = self.dest.data[idx + 1];
            self.src.data[idx2 + 2] = self.dest.data[idx + 2];
        }
    }
    self.ctx.putImageData(self.src, 0, 0);
    self.canvas.style.display = "block";
}
});

#4樓

Fast image resize/resample algorithm using Hermite filter with JavaScript. 使用帶有JavaScript的Hermite過濾器的快速圖像調整大小/重採樣算法。 Support transparency, gives good quality. 支持透明,提供良好的質量。 Preview: 預習:

在此處輸入圖片說明

Update : version 2.0 added on GitHub (faster, web workers + transferable objects). 更新 :在GitHub上添加了2.0版(更快,網絡工作者+可轉移對象)。 Finally i got it working! 終於我得到了它的工作!

Git: https://github.com/viliusle/Hermite-resize Git: https : //github.com/viliusle/Hermite-resize
Demo: http://viliusle.github.io/miniPaint/ 演示: http//viliusle.github.io/miniPaint/

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

#5樓

If you're simply trying to resize an image, I'd recommend setting width and height of the image with CSS. 如果您只是想調整圖像的大小,建議您使用CSS設置圖像的widthheight Here's a quick example: 這是一個簡單的示例:

.small-image {
    width: 100px;
    height: 100px;
}

Note that the height and width can also be set using JavaScript. 注意, heightwidth也可以使用JavaScript進行設置。 Here's quick code sample: 這是快速的代碼示例:

var img = document.getElement("my-image");
img.style.width = 100 + "px";  // Make sure you add the "px" to the end,
img.style.height = 100 + "px"; // otherwise you'll confuse IE

Also, to ensure that the resized image looks good, add the following css rules to image selector: 另外,爲確保調整後的圖像看起來不錯,請在圖像選擇器中添加以下css規則:

As far as I can tell, all browsers except IE using an bicubic algorithm to resize images by default, so your resized images should look good in Firefox and Chrome. 據我所知,默認情況下,除IE以外的所有瀏覽器均使用雙三次算法來調整圖像大小,因此,在Firefox和Chrome瀏覽器中,調整大小後的圖像應該看起來不錯。

If setting the css width and height doesn't work, you may want to play with a css transform : 如果設置CSS的widthheight不起作用,則可能需要使用CSS transform

If for whatever reason you need to use a canvas, please note that there are two ways an image can be resize: by resizing the canvas with css or by drawing the image at a smaller size. 如果出於任何原因需要使用畫布,請注意可以通過兩種方式調整圖像的大小:使用CSS調整畫布的大小或以較小的尺寸繪製圖像。

See this question for more details. 有關更多詳細信息,請參見此問題


#6樓

i got this image by right clicking the canvas element in firefox and saving as. 我通過右鍵單擊firefox中的canvas元素並將其另存爲得到此圖像。

替代文字

var img = new Image();
img.onload = function () {
    console.debug(this.width,this.height);
    var canvas = document.createElement('canvas'), ctx;
    canvas.width = 188;
    canvas.height = 150;
    document.body.appendChild(canvas);
    ctx = canvas.getContext('2d');
    ctx.drawImage(img,0,0,188,150);
};
img.src = 'original.jpg';

so anyway, here is a 'fixed' version of your example: 因此,無論如何,這是示例的“固定”版本:

var img = new Image();
// added cause it wasnt defined
var canvas = document.createElement("canvas");
document.body.appendChild(canvas);

var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
// adding it to the body

document.body.appendChild(canvasCopy);

var copyContext = canvasCopy.getContext("2d");

img.onload = function()
{
        var ratio = 1;

        // defining cause it wasnt
        var maxWidth = 188,
            maxHeight = 150;

        if(img.width > maxWidth)
                ratio = maxWidth / img.width;
        else if(img.height > maxHeight)
                ratio = maxHeight / img.height;

        canvasCopy.width = img.width;
        canvasCopy.height = img.height;
        copyContext.drawImage(img, 0, 0);

        canvas.width = img.width * ratio;
        canvas.height = img.height * ratio;
        // the line to change
        // ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
        // the method signature you are using is for slicing
        ctx.drawImage(canvasCopy, 0, 0, canvas.width, canvas.height);
};

// changed for example
img.src = 'original.jpg';
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章