HTML5キャンバスでの画像のサイズ変更


314

javascriptとcanvas要素を使用してクライアント側でサムネイル画像を作成しようとしていますが、画像を縮小するとひどく見えます。リサンプリングがバイキュービックではなく「ニアレストネイバー」に設定されているため、フォトショップで縮小されたように見えます。このサイトを正しく表示することは可能です。はキャンバスを使用しても問題なく実行できるすることは可能です。「[Source]」リンクに示されているのと同じコードを使用してみましたが、それでもひどいようです。私が見逃しているもの、設定する必要のある設定などはありますか?

編集:

jpgのサイズを変更しようとしています。リンク先のサイトとフォトショップで同じjpgのサイズを変更してみましたが、サイズを小さくすると問題ありません。

関連するコードは次のとおりです。

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:

誤解されているようですが、リンク先のWebサイトは画像のサイズを小さくする作業をうまく行っていませんでした。私は提案された他の方法を試してみましたが、どれもよく見えません。これは、さまざまな方法で生じたものです。

Photoshop:

代替テキスト

キャンバス:

代替テキスト

画像レンダリングを使用した画像:最適化品質を設定し、幅/高さでスケーリング:

代替テキスト

画像レンダリングを使用した画像:optimizeQualityが設定され、-moz-transformでスケーリングされます:

代替テキスト

pixasticでのキャンバスのサイズ変更:

代替テキスト

これは、Firefoxが本来のようにバイキュービックサンプリングを使用していないことを意味します。実際に追加されるまで待つ必要があります。

EDIT3:

元の画像


画像のサイズ変更に使用しているコードを投稿できますか?
Xavi

限られたパレットでGIF画像または同様の画像のサイズを変更しようとしていますか?photoshopでも、RGBに変換しない限り、これらの画像はうまく縮小できません。
leepowers 2010

元の画像のコピーを投稿できますか?
ザビ

JavaScriptを使用して画像のサイズを変更するのは少々厄介です。画像のサイズを変更するためにクライアントの処理能力を使用しているだけでなく、ページを読み込むたびにそれを実行しています。縮小したバージョンをフォトショップから保存して、代わりに/元の画像と一緒に提供しないのはなぜですか?
定義

29
アップロードする前に画像のサイズを変更してトリミングできる画像アップローダーを作成しているからです。
Telanor

回答:


393

では、すべてのブラウザ(実際にはChrome 5でかなり良いブラウザが提供された)で十分なリサンプリング品質が得られない場合はどうしますか?その後、自分で実装します!さあ、私たちはWeb 3.0、HTML5準拠のブラウザー、超最適化されたJIT JavaScriptコンパイラー、大量のメモリを搭載したマルチコア(†)マシンの新時代を迎えています。何を恐れていますか?こんにちは、javascriptにjavaという単語があるので、パフォーマンスが保証されますよね 見よ、サムネイル生成コード:

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

// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
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(this.process1, 0, this, 0);
}

thumbnailer.prototype.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(self.process1, 0, self, u);
    else
        setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
    self.canvas.width = self.dest.width;
    self.canvas.height = self.dest.height;
    self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
    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";
};

...これらのような結果を生成できます!

img717.imageshack.us/img717/8910/lanczos358.png

とにかく、ここにあなたの例の「修正された」バージョンがあります:

img.onload = function() {
    var canvas = document.createElement("canvas");
    new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
    // but feel free to raise it up to 8. Your client will appreciate
    // that the program makes full use of his machine.
    document.body.appendChild(canvas);
};

さあ、あなたの最高のブラウザを試してみて、クライアントの血圧を上げる可能性が最も低いブラウザを見てみましょう!

えーと、皮肉のタグはどこ?

(コードの多くの部分はAnrieff Gallery Generatorに基づいているためいるため、GPL2にも対応していますか?ません)

実際にはJavaScriptの制限により、マルチコアはサポートされていません。


2
私は実際にそれを自分で実装しようとしましたが、オープンソースの画像エディターからコードをコピーしました。アルゴリズムに関する確かなドキュメントを見つけることができなかったので、アルゴリズムを最適化するのに苦労しました。結局、私のものはちょっと遅い(画像のサイズを変更するのに数秒かかった)。機会がありましたら、私はあなたのものを試して、それがもっと速いかどうかを確認します。そして今、ウェブワーカーはマルチコアJavaScriptを可能にしていると思います。それらを使用して高速化を試みるつもりでしたが、これをマルチスレッドアルゴリズムにする方法を理解するのに苦労していました
Telanor

3
すみません、忘れました!返信を編集しました。とにかく速くなることはありません、バイキュービックは速くなるはずです。言うまでもなく、私が使用したアルゴリズムは通常の2方向のサイズ変更(行ごと、水平、垂直)でなく、そのため、処理が遅くなります。
syockit 2010

5
あなたは素晴らしく、たくさんの素晴らしさに値します。
ロックラン、2013

5
これはまともな結果を生成しますが、Chromeの最新バージョンで1.8 MPの画像を
取得

2
このような方法はどのようにそのようなハイスコアを管理しますか?示されているソリューションは、色情報の保存に使用される対数スケールを完全に考慮していません。127、127、127のRGBは、255、255、255の輝度の4分の1であり、半分ではありません。ソリューションのダウンサンプリングにより、画像が暗くなります。残念なことに、Photoshopよりも優れた結果が得られる非常にシンプルで迅速なサイズダウン方法があるため、これは閉じています(OPの設定が間違っている必要があります)
Blindman67

37

JavaScriptでHermiteフィルターを使用した高速画像サイズ変更/リサンプルアルゴリズム。透明度をサポートし、良い品質を提供します。プレビュー:

ここに画像の説明を入力してください

更新:GitHubにバージョン2.0が追加されました(高速、ウェブワーカー+転送可能なオブジェクト)。ついに私はそれを働かせました!

Git:https : //github.com/viliusle/Hermite-resize
Demo: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);
}

多分あなたはあなたのminiPaintデモとGithubレポへのリンクを含めることができますか?
syockit 2013

1
ウェブワーカーバージョンも共有しますか?おそらく、セットアップのオーバーヘッドが原因で、小さな画像の場合は遅くなりますが、大きなソース画像の場合は役立ちます。
syockit 2013

デモ、gitリンク、マルチコアバージョンも追加。ところで、マルチコアバージョンの最適化にあまり時間をかけていませんでした...シングルバージョンは最適化されていると思います。
ViliusL 2013

大きな違いとまともなパフォーマンス。どうもありがとうございました!
KevBurnsJr

2
@ViliusLああ、Webワーカーがうまく機能しなかった理由を思い出しました。彼らは以前に共有メモリを持っていなかったし、今でもまだ持っていない!多分いつか彼らがそれを整理するために管理するとき、あなたのコードは使用するようになるでしょう(あるいは、多分人々は代わりにPNaClを使用します)
syockit

26

picaを試してください。これは、アルゴリズムを選択できる高度に最適化されたリサイザです。デモを見る

たとえば、最初の投稿の元の画像は、Lanczosフィルターと3pxウィンドウで120ms、Boxフィルターと0.5pxウィンドウで60msにサイズ変更されます。17MBの巨大な画像の場合、5000x3000pxのサイズ変更には、デスクトップで1秒、モバイルで3秒かかります。

すべてのサイズ変更の原則はこのスレッドで非常によく説明されており、パイカはロケット科学を追加しません。ただし、最新のJIT向けに最適化されており、(npmまたはbowerを介して)すぐに使用できます。また、インターフェースのフリーズを回避するために、使用可能な場合はWebワーカーを使用します。

アンスケールマスクのサポートもすぐに追加する予定です。ダウンスケール後は非常に便利だからです。


14

私はこれが古いスレッドであることを知っていますが、この問題に初めて遭遇した数か月後に私のような一部の人にとって役立つかもしれません。

以下は、画像をリロードするたびに画像のサイズを変更するコードです。これは最適ではないことは承知していますが、概念実証として提供しています。

また、単純なセレクターにjQueryを使用することは申し訳ありませんが、構文に慣れすぎています。

$(document).on('ready', createImage);
$(window).on('resize', createImage);

var createImage = function(){
  var canvas = document.getElementById('myCanvas');
  canvas.width = window.innerWidth || $(window).width();
  canvas.height = window.innerHeight || $(window).height();
  var ctx = canvas.getContext('2d');
  img = new Image();
  img.addEventListener('load', function () {
    ctx.drawImage(this, 0, 0, w, h);
  });
  img.src = 'http://www.ruinvalor.com/Telanor/images/original.jpg';
};
html, body{
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
  background: #000;
}
canvas{
  position: absolute;
  left: 0;
  top: 0;
  z-index: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Canvas Resize</title>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

私のcreateImage関数は、ドキュメントが読み込まれたときに一度呼び出され、その後、ウィンドウがサイズ変更イベントを受け取るたびに呼び出されます。

私はそれを両方ともMac上のChrome 6とFirefox 3.6でテストしました。この「テクニック」は、まるで夏のアイスクリームのようにプロセッサーを食べますが、トリックを行います。


9

ここで役立つかもしれないhtmlキャンバスピクセル配列で画像補間を行うアルゴリズムをいくつか用意しました。

https://web.archive.org/web/20170104190425/http://jsperf.com:80/pixel-interpolation/2

これらはコピー/貼り付けでき、Webワーカー内で使用して画像のサイズを変更できます(または、補間が必要なその他の操作-現在、画像のデフィッシュに使用しています)。

上記のlanczosは追加していません。必要に応じて、比較として追加してください。


6

これは、@ Telanorのコードを改造したJavaScript関数です。画像のbase64を関数の最初の引数として渡すと、サイズが変更された画像のbase64が返されます。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();

}

あなたのアプローチは非常に高速ですが、あなたがここに見ることができるように、ファジー画像を生成:stackoverflow.com/questions/18922880/...
confile

6

このリンクをチェックして、trueに設定されていることを確認することを強くお勧めします。

画像スケーリング動作の制御

Gecko 1.9.2で導入(Firefox 3.6 / Thunderbird 3.1 / Fennec 1.0)

Gecko 1.9.2は、mozImageSmoothingEnabledプロパティをキャンバス要素に導入しました。このブール値がfalseの場合、画像は拡大縮小されても平滑化されません。このプロパティはデフォルトでtrueです。プレーンプリントを表示しますか?

  1. cx.mozImageSmoothingEnabled = false;

5

画像のサイズを変更するだけの場合は、CSSを使用して画像を設定することwidthをお勧めしますheight。ここに簡単な例があります:

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

ことに注意してくださいheightとはwidthまたJavaScriptを使用して設定することができます。ここに簡単なコードサンプルがあります:

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

また、サイズ変更された画像が適切に表示されるようにするには、次のCSSルールを画像セレクターに追加します。

私の知る限り、IEを除くすべてのブラウザーはバイキュービックアルゴリズムを使用してデフォルトで画像のサイズを変更しているため、サイズ変更した画像はFirefoxとChromeで適切に表示されます。

CSS widthを設定してもheight機能しない場合は、CSSで遊んでみてくださいtransform

何らかの理由でキャンバスを使用する必要がある場合は、画像のサイズを変更できる2つの方法があることに注意してください。CSSでキャンバスのサイズを変更する方法と、画像を小さいサイズで描画する方法です。

詳細については、この質問を参照してください。


5
残念ながら、キャンバスのサイズを変更したり、画像を小さいサイズで描画したりしても、問題が解決しません(Chromeの場合)。
Nestor

1
Chrome 27は適切なサイズ変更画像を生成しますが、結果をキャンバスにコピーすることはできません。そうしようとすると、代わりに元の画像がコピーされます。
syockit 2013

4

元の幅よりも小さい幅の画像にサイズ変更するには、次を使用します:

    function resize2(i) {
      var cc = document.createElement("canvas");
      cc.width = i.width / 2;
      cc.height = i.height / 2;
      var ctx = cc.getContext("2d");
      ctx.drawImage(i, 0, 0, cc.width, cc.height);
      return cc;
    }
    var cc = img;
    while (cc.width > 64 * 2) {
      cc = resize2(cc);
    }
    // .. than drawImage(cc, .... )

そしてそれは動作します=)。


4

この画像は、Firefoxでキャンバス要素を右クリックして保存しました。

代替テキスト

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';

とにかく、ここにあなたの例の「修正された」バージョンがあります:

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';

私はあなたがしたことをやってみましたが、あなたのようにうまくいきません。私が何かを見逃していない限り、あなたが行った唯一の変更は、スライスのシグネチャではなくスケーリングメソッドのシグネチャを使用することでしたよね?どういうわけかそれは私のために働いていません。
Telanor 2010年

3

このソリューションのいくつかの問題は、ピクセルデータに直接アクセスし、それをループしてダウンサンプリングを実行することです。画像のサイズによっては、これは非常に多くのリソースを消費する可能性があり、ブラウザの内部アルゴリズムを使用することをお勧めします。

drawImage()関数は、メソッドをリサンプリング、線形補間、最近傍を使用しています。これは、元のサイズの半分以上のサイズを縮小しない場合にうまく機能します

一度に最大半分だけサイズ変更するようにループする場合、結果は非常に良好で、ピクセルデータにアクセスするよりもはるかに高速です。

この関数は、目的のサイズに達するまで一度に半分にダウンサンプリングします。

  function resize_image( src, dst, type, quality ) {
     var tmp = new Image(),
         canvas, context, cW, cH;

     type = type || 'image/jpeg';
     quality = quality || 0.92;

     cW = src.naturalWidth;
     cH = src.naturalHeight;

     tmp.src = src.src;
     tmp.onload = function() {

        canvas = document.createElement( 'canvas' );

        cW /= 2;
        cH /= 2;

        if ( cW < src.width ) cW = src.width;
        if ( cH < src.height ) cH = src.height;

        canvas.width = cW;
        canvas.height = cH;
        context = canvas.getContext( '2d' );
        context.drawImage( tmp, 0, 0, cW, cH );

        dst.src = canvas.toDataURL( type, quality );

        if ( cW <= src.width || cH <= src.height )
           return;

        tmp.src = dst.src;
     }

  }
  // The images sent as parameters can be in the DOM or be image objects
  resize_image( $( '#original' )[0], $( '#smaller' )[0] );

この投稿のクレジット


2

キャンバスを操作しているときに少し前に見つけた興味深いものは、役に立つかもしれません。

キャンバスコントロールのサイズを独自に変更するには、height=""およびwidth=""属性(またはcanvas.width/canvas.height要素)。CSSを使用してキャンバスのサイズを変更すると、キャンバスのコンテンツが実際に拡大(つまり、サイズ変更)され、キャンバス全体が収まるようになります(単にキャンバスの領域を拡大または縮小するのではなく)。

高さと幅の属性を画像のサイズに設定してキャンバスコントロールに画像を描画し、CSSを使用してキャンバスを目的のサイズにサイズ変更することをお勧めします。おそらく、これは異なるサイズ変更アルゴリズムを使用します。

また、キャンバスはブラウザによって(また、ブラウザのバージョンによっては)効果が異なることにも注意してください。ブラウザーで使用されるアルゴリズムと手法は、時間の経過とともに変化する可能性があります(特に、Firefox 4とChrome 6が間もなく登場するため、キャンバスのレンダリングパフォーマンスに重点が置かれます)。

さらに、SVGも別のアルゴリズムを使用している可能性が高いため、SVGにも試してみることをお勧めします。

がんばって!


1
HTML属性を使用してキャンバスの幅または高さを設定すると、キャンバスがクリアされるため、そのメソッドでサイズ変更を行うことはできません。また、SVGは数学画像を処理するためのものです。私はPNGなどを描くことができるようにする必要があるので、それが私を助けることはありません。
Telanor 2010

キャンバスの高さと幅を設定し、CSSを使用してサイズを変更しても役に立たないことがわかりました(Chromeの場合)。CSSの幅/高さではなく-webkit-transformを使用してサイズ変更を行っても、補間は行われません。
ネストル

2

アルゴリズムを適用するのではなく、平均化することでカラーデータを保存するため、私が作成したモジュールはphotoshopと同様の結果を生成すると感じています。少し遅いですが、すべての色データが保持されるので、私にとっては最高です。

https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js

最近傍を取得して他のピクセルをドロップすることも、グループをサンプリングしてランダム平均を取得することもありません。これは、各ソースピクセルが宛先ピクセルに出力する正確な比率をとります。ソース内の平均ピクセルカラーは、宛先内の平均ピクセルカラーになります。これらの他の公式では、そうではないと思います。

使用方法の例は、https://github.com/danschumann/limby-resizeの下部にあり ます

2018年10更新:最近の私の例は、何よりも学術的です。Webglはほぼ100%なので、同様の結果を生成するためにサイズを変更した方がよいでしょうが、より高速です。PICA.jsはこれを行うと思います。–


1

@syockitの回答とステップダウンアプローチを、興味のある人のための再利用可能なAngularサービスに変換しました:https : //gist.github.com/fisch0920/37bac5e741eaec60e983

どちらにも独自の長所と短所があるため、両方のソリューションを含めました。lanczosコンボリューションアプローチは品質が高く、速度は遅くなりますが、段階的なダウンスケーリングアプローチは適度にアンチエイリアス処理された結果を生成し、非常に高速です。

使用例:

angular.module('demo').controller('ExampleCtrl', function (imageService) {
  // EXAMPLE USAGE
  // NOTE: it's bad practice to access the DOM inside a controller, 
  // but this is just to show the example usage.

  // resize by lanczos-sinc filter
  imageService.resize($('#myimg')[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })

  // resize by stepping down image size in increments of 2x
  imageService.resizeStep($('#myimg')[0], 256, 256)
    .then(function (resizedImage) {
      // do something with resized image
    })
})

1

高速でシンプルなJavascript画像リサイザ:

https://github.com/calvintwr/blitz-hermite-resize

const blitz = Blitz.create()

/* Promise */
blitz({
    source: DOM Image/DOM Canvas/jQuery/DataURL/File,
    width: 400,
    height: 600
}).then(output => {
    // handle output
})catch(error => {
    // handle error
})

/* Await */
let resized = await blizt({...})

/* Old school callback */
const blitz = Blitz.create('callback')
blitz({...}, function(output) {
    // run your callback.
})

歴史

これは、実際に何度も調査、読書、試行を重ねた結果です。

リサイザアルゴリズムは、@ ViliusLのHermiteスクリプトを使用します(Hermiteリサイザは本当に最速で、適度に優れた出力を提供します)。必要な機能で拡張されています。

他のすべてのJSリサイザーとは異なり、サイズ変更時にブラウザーがフリーズしないように、1人のワーカーにサイズ変更を依頼します。


0

すばらしい答えをありがとう@syockit。ただし、それを機能させるには、次のように少し再フォーマットする必要がありました。おそらく、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";
}
});

-1

私は並べて比較のページを実行しただけで、最近何かが変更されない限り、キャンバスと単純なcssを使用したより良いダウンサイジング(スケーリング)を見ることができませんでした。FF6 Mac OSX 10.7でテストしました。オリジナルと比べて、ややソフトです。

しかし、私は大きな違いを生み、キャンバスをサポートするブラウザーで画像フィルターを使用している何かに出会いました。Photoshopの場合と同じように、ぼかし、鮮鋭化、彩度、リップル、グレースケールなどで画像を実際に操作できます。

次に、これらのフィルターの適用を簡単にする素晴らしいjQueryプラグインを見つけました:http : //codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

画像のサイズを変更した直後にシャープニングフィルターを適用するだけで、目的の効果が得られます。canvas要素を使用する必要さえありませんでした。


-1

別の素晴らしいシンプルなソリューションをお探しですか?

var img=document.createElement('img');
img.src=canvas.toDataURL();
$(img).css("background", backgroundColor);
$(img).width(settings.width);
$(img).height(settings.height);

このソリューションは、ブラウザのサイズ変更アルゴリズムを使用します!:)


問題は、画像のサイズ変更だけでなく、画像のダウンサンプリングについてです。
ヘスス・カレラ

[...] jpgのサイズを変更しようとしています。リンク先のサイトとフォトショップで同じjpgのサイズを変更してみましたが、縮小すると問題なく見えます。[...] <img>ジーザスカレラを使用できない理由
ale500 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.