HTML5 कैनवास में एक छवि का आकार बदलना


314

मैं जावास्क्रिप्ट और एक कैनवास तत्व का उपयोग करके क्लाइंट की तरफ एक थंबनेल छवि बनाने की कोशिश कर रहा हूं, लेकिन जब मैं छवि को छोटा करता हूं, तो यह भयानक लगता है। ऐसा लग रहा है कि यह फोटोशॉप में बेज़ुबिक के बजाय 'नियरेस्ट नेबर' के सेट पर दोबारा सेट किया गया था। मुझे यह पता है कि यह सही लगने के लिए संभव है, क्योंकि यह साइट एक कैनवास का उपयोग करके इसे ठीक कर सकती है। मैंने उसी कोड का उपयोग करने की कोशिश की है जो वे "[स्रोत]" लिंक में दिखाए गए हैं, लेकिन यह अभी भी भयानक दिखता है। क्या मुझे कुछ याद आ रहा है, कुछ सेटिंग जो सेट करने की ज़रूरत है या कुछ और?

संपादित करें:

मैं एक 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:

लगता है कि मुझसे गलती हुई थी, लिंक की गई वेबसाइट छवि को खराब करने के काम से बेहतर नहीं कर रही थी। मैंने सुझाए गए अन्य तरीकों की कोशिश की और उनमें से कोई भी बेहतर नहीं दिखता। विभिन्न तरीकों के परिणामस्वरूप यह है:

फोटोशॉप:

वैकल्पिक शब्द

कैनवस:

वैकल्पिक शब्द

इमेज-इमेज विथ इमेज-रेंडरिंग: ऑप्टिमाइज़्युअलिटी सेट एंड स्केल्ड विथ चौड़ाई / हाइट:

वैकल्पिक शब्द

छवि-प्रतिपादन के साथ छवि: ऑप्टिमाइज़ेशन सेट और स्केल -मोज़-परिवर्तन के साथ:

वैकल्पिक शब्द

कैनवस पिक्सैस्टिक पर आकार देता है:

वैकल्पिक शब्द

मुझे लगता है कि इसका मतलब है कि फ़ायरफ़ॉक्स अपनी पसंद की तरह bicubic नमूने का उपयोग नहीं कर रहा है। मुझे बस इंतजार करना होगा जब तक वे वास्तव में इसे नहीं जोड़ते।

EDIT3:

मूल छवि


क्या आप उस कोड को पोस्ट कर सकते हैं जिसका उपयोग आप छवि को आकार देने के लिए कर रहे हैं?
Xavi

क्या आप एक सीमित पैलेट के साथ GIF छवि या समान छवि का आकार बदलने की कोशिश कर रहे हैं? यहां तक ​​कि जब तक आप उन्हें RGB में नहीं बदल देते, तब तक ये फोटोशॉप अच्छी तरह से स्केल नहीं करते हैं।
लेपॉवर

क्या आप मूल छवि की एक प्रति पोस्ट कर सकते हैं?
१३:३१

जावास्क्रिप्ट का उपयोग करके छवि का आकार बदलना थोड़ा कठिन है - न केवल आप छवि को आकार देने के लिए क्लाइंट प्रोसेसिंग पावर का उपयोग कर रहे हैं, आप इसे हर एक पृष्ठ लोड पर कर रहे हैं। क्यों नहीं बस एक नीचे संस्करण को फ़ोटोशॉप से ​​बचाया और इसके बजाय / मूल छवि के साथ मिलकर सेवा की?
परिभाषित करता है

29
क्योंकि मैं छवियों को आकार देने और उन्हें अपलोड करने से पहले छवियों को क्रॉप करने की क्षमता के साथ एक अपलोडर बना रहा हूं।
तेलनोर

जवाबों:


393

तो आप क्या करते हैं यदि सभी ब्राउज़र (वास्तव में, Chrome 5 ने मुझे काफी अच्छा दिया है) आपको अच्छी पर्याप्त गुणवत्ता प्रदान नहीं करेगा? आप उन्हें स्वयं लागू करें! ओह, हम वेब 3.0 के नए युग में प्रवेश कर रहे हैं, HTML5 अनुरूप ब्राउज़रों, सुपर अनुकूलित JIT जावास्क्रिप्ट कम्पाइलर, मल्टी-कोर (†) मशीनों, टन स्मृति के साथ, आप किससे डरते हैं? अरे, जावास्क्रिप्ट में जावा शब्द है, इसलिए प्रदर्शन की गारंटी चाहिए, है ना? निहारना, थंबनेल उत्पन्न कोड:

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

अब यह समय है कि आप अपने सर्वश्रेष्ठ ब्राउज़रों को गड्ढे में डाल दें और देखें कि कौन सा कम से कम आपके ग्राहक के रक्तचाप को बढ़ाएगा!

उम्म, मेरा व्यंग्य टैग कहां है?

(चूँकि कोड के कई भाग Anffff Gallery जनरेटर पर आधारित है, क्या यह GPL2 के अंतर्गत आता है? I dunno)

वास्तव में जावास्क्रिप्ट की सीमा के कारण, मल्टी-कोर समर्थित नहीं है।


2
मैंने वास्तव में इसे लागू करने की कोशिश की थी, जैसा कि आपने किया, एक खुले स्रोत छवि संपादक से कोड की नकल करना। चूंकि मैं एल्गोरिथ्म पर कोई ठोस दस्तावेज नहीं ढूंढ पाया था, इसलिए मुझे इसके अनुकूलन में कठिन समय था। अंत में, मेरी तरह धीमी थी (छवि को आकार देने में कुछ सेकंड लगे)। जब मुझे मौका मिलता है, तो मैं आपकी कोशिश करूंगा और देखूंगा कि क्या यह और तेज है और मुझे लगता है कि वेबवर्क अब मल्टी-कोर जावास्क्रिप्ट को संभव बनाते हैं। मैं इसे तेज करने के लिए उनका उपयोग करने की कोशिश करने जा रहा था, लेकिन मुझे यह समझने में परेशानी हो रही थी कि इसे मल्टीथ्रेडेड एल्गोरिथ्म में कैसे बनाया जाए
Telanor

3
क्षमा करें, भूल गए! मैंने उत्तर संपादित कर दिया है। यह तेजी से वैसे भी नहीं होने जा रहा है, बाइसिक्योर तेज होना चाहिए। मेरे द्वारा उपयोग किए जाने वाले एल्गोरिदम का उल्लेख नहीं करना सामान्य रूप से 2-वे आकार का नहीं है (जो लाइन द्वारा लाइन है, क्षैतिज तो ऊर्ध्वाधर), इसलिए यह एक लूप स्लोअर है।
सोकॉइट

5
आप भयानक हैं और कई टन के लायक हैं।
रॉकलन

5
यह अच्छे परिणाम देता है, लेकिन क्रोम के नवीनतम संस्करण में 1.8 MP की छवि के लिए 7.4 सेकंड का समय लगता है ...
MPen

2
इस तरह के तरीके ऐसे उच्च स्कोर को कैसे प्रबंधित करते हैं ?? दिखाया गया समाधान रंग जानकारी को संग्रहीत करने के लिए उपयोग किए जाने वाले लघुगणक पैमाने के लिए पूरी तरह से विफल रहता है। 127,127,127 का आरजीबी 255, 255, 255 नहीं आधा का एक चौथाई चमक है। समाधान में नीचे नमूना लेने से एक अंधेरे छवि होती है। यह शर्म की बात है क्योंकि नीचे आकार के लिए एक बहुत ही सरल और त्वरित तरीका है जो फ़ोटोशॉप की तुलना में भी बेहतर परिणाम देता है (ओपी को वरीयताएँ गलत होना चाहिए) नमूना दिया गया था
ब्लाइंडमान 67

37

जावास्क्रिप्ट के साथ हरमाइट फ़िल्टर का उपयोग करके तेज़ छवि का आकार बदलना / पुन: कॉन्फ़िगर करना। पारदर्शिता का समर्थन, अच्छी गुणवत्ता देता है। पूर्वावलोकन:

यहां छवि विवरण दर्ज करें

अद्यतन : संस्करण 2.0 GitHub (तेजी से, वेब श्रमिकों + हस्तांतरणीय वस्तुओं) पर जोड़ा गया। अंत में मैंने इसे काम कर लिया!

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

शायद आप अपने मिनीपैंट डेमो और जीथब रेपो के लिंक शामिल कर सकते हैं?
syockit

1
क्या आप वेबवर्कर्स संस्करण भी साझा करेंगे? संभवतः सेटअप ओवरहेड के कारण, यह छोटी छवियों के लिए धीमा है, लेकिन यह बड़े स्रोत चित्रों के लिए उपयोगी हो सकता है।
syockit

जोड़ा गया डेमो, गिट लिंक, मल्टी-कोर संस्करण भी। Btw मैं मल्टीकोर संस्करण के अनुकूलन पर बहुत अधिक समय खर्च नहीं किया ... मुझे लगता है कि एकल संस्करण अच्छी तरह से अनुकूलित है।
विलीसुएल

विशाल अंतर और सभ्य प्रदर्शन। आपका बहुत बहुत धन्यवाद! इससे पहले और बाद में
KevBurnsJr

2
@ViliusL आह अब मुझे याद आया कि वेब कर्मचारियों ने इतना अच्छा काम क्यों नहीं किया। उन्होंने पहले मेमोरी साझा नहीं की थी, और अब भी नहीं है! शायद किसी दिन जब वे इसे छांटने का प्रबंधन करते हैं, तो आपका कोड उपयोग करने के लिए आएगा (कि, या शायद लोग इसके बजाय PNaCl का उपयोग करें)
syockit

26

पिका की कोशिश करें - यह चयन योग्य अल्गोरिथम के साथ एक अत्यधिक अनुकूलित पुनर्विक्रेता है। डेमो देखें ।

उदाहरण के लिए, पहली पोस्ट से मूल छवि लैंसजोस फिल्टर के साथ 120ms और बॉक्स फिल्टर और 0.5px विंडो के साथ 3ms खिड़की या 60ms के साथ आकार में है। विशाल 17mb की छवि के लिए 5000x3000px आकार डेस्कटॉप पर ~ 1s और मोबाइल पर 3s लेता है।

इस धागे में सभी आकार सिद्धांतों का बहुत अच्छी तरह से वर्णन किया गया था, और पिका रॉकेट विज्ञान को नहीं जोड़ता है। लेकिन यह आधुनिक JIT-s के लिए बहुत अच्छी तरह से अनुकूलित है, और आउट ऑफ बॉक्स (npm या bower के माध्यम से) उपयोग करने के लिए तैयार है। इसके अलावा, इंटरफ़ेस फ्रीज़ से बचने के लिए उपलब्ध होने पर यह वेबवर्क का उपयोग करता है।

मैं जल्द ही अनसर्प मास्क सपोर्ट जोड़ने की योजना भी बना रहा हूं, क्योंकि डाउनस्केल के बाद यह बहुत उपयोगी है।


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 फ़ंक्शन को दस्तावेज़ लोड होने के बाद एक बार कॉल किया जाता है और उसके बाद हर बार विंडो को रिसाइज़ इवेंट प्राप्त होने पर इसे कॉल किया जाता है।

मैंने इसे क्रोम 6 और फ़ायरफ़ॉक्स 3.6, मैक पर दोनों में परीक्षण किया। यह "तकनीक" प्रोसेसर खाती है जैसे कि गर्मियों में आइसक्रीम थी, लेकिन यह चाल करता है।


9

मैंने HTML कैनवास पिक्सेल सरणियों पर छवि प्रक्षेप करने के लिए कुछ एल्गोरिदम लगाए हैं जो यहां उपयोगी हो सकते हैं:

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

इन्हें कॉपी किया जा सकता है / चिपकाया जा सकता है और वेब श्रमिकों के अंदर उपयोग किया जा सकता है छवियों का आकार बदलने के लिए (या किसी अन्य ऑपरेशन जिसमें प्रक्षेप की आवश्यकता होती है - मैं उनका उपयोग फिलहाल छवियों को नष्ट करने के लिए कर रहा हूं)।

मैंने ऊपर लैंसजोस सामान नहीं जोड़ा है, इसलिए यदि आप चाहें तो तुलना के रूप में इसे जोड़ने के लिए स्वतंत्र महसूस करें।


6

यह @ Telanor के कोड से अनुकूलित एक जावास्क्रिप्ट फ़ंक्शन है। फ़ंक्शन के पहले तर्क के रूप में एक इमेज बेस 64 पास करते समय, यह रिसाइज्ड इमेज के बेस 64 को वापस कर देता है। मैक्सविथ और मैक्सहाइट वैकल्पिक हैं।

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

6

मैं आपको अत्यधिक सुझाव दूंगा कि आप इस लिंक को देखें और सुनिश्चित करें कि यह सत्य पर सेट है।

छवि स्केलिंग व्यवहार को नियंत्रित करना

गेको 1.9.2 (फ़ायरफ़ॉक्स 3.6 / थंडरबर्ड 3.1 / फेनेक 1.0) में पेश किया गया

गेको 1.9.2 ने कैनवास तत्व में mozImageSmoothingEnabled संपत्ति पेश की; यदि यह बूलियन मान गलत है, तो स्केल किए जाने पर चित्र सुचारू नहीं किए जाएंगे। यह गुण डिफ़ॉल्ट रूप से सत्य है। सादा दृश्य?

  1. cx.mozImageSmoothingEnabled = false;

5

यदि आप बस एक छवि का आकार बदलने की कोशिश कर रहे हैं, तो मैं CSS के साथ छवि की सेटिंग widthऔर उपयोग करने heightकी सलाह दूंगा। यहाँ एक त्वरित उदाहरण है:

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

ध्यान दें कि heightऔर widthजावास्क्रिप्ट का उपयोग करके भी सेट किया जा सकता है। यहाँ त्वरित कोड नमूना है:

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

इसके अलावा, यह सुनिश्चित करने के लिए कि संशोधित छवि अच्छी दिखती है, छवि चयनकर्ता के लिए निम्नलिखित सीएसएस नियम जोड़ें:

जहाँ तक मैं बता सकता हूँ, IE के अलावा सभी ब्राउज़रों डिफ़ॉल्ट रूप से छवियों का आकार बदलने के लिए एक bicubic एल्गोरिथ्म का उपयोग कर, इसलिए आपके resized छवियों फ़ायरफ़ॉक्स और क्रोम में अच्छा दिखना चाहिए।

यदि सीएसएस सेट करना widthऔर heightकाम नहीं करता है, तो आप सीएसएस के साथ खेलना चाह सकते हैं transform:

अगर किसी भी कारण से आपको जरूरत है कैनवास का उपयोग करने की है, तो कृपया ध्यान दें कि दो तरीके हैं जिससे एक छवि को आकार दिया जा सकता है: कैनवास को सीएसएस के साथ आकार देकर या छोटे आकार में छवि को खींचकर।

देखें इस सवाल का अधिक जानकारी के लिए।


5
न तो कैनवास का आकार बदलना और न ही छोटे आकार में चित्र बनाना समस्या को हल करता है (क्रोम में), दुख की बात है।
नेस्टर

1
Chrome 27 अच्छी आकार वाली छवि बनाता है, लेकिन आप परिणाम को एक कैनवास पर कॉपी नहीं कर सकते; ऐसा करने का प्रयास इसके बजाय मूल छवि को कॉपी करेगा।
--१३

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

मैं फ़ायरफ़ॉक्स में कैनवास तत्व को राइट क्लिक करके और के रूप में सहेजकर इस छवि को प्राप्त किया।

वैकल्पिक शब्द

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

मैंने वह करने की कोशिश की है जो आपने किया है और यह आपकी तरह अच्छा नहीं आ रहा है। जब तक मैं कुछ याद नहीं करता, तब तक आपके द्वारा किए गए एकमात्र परिवर्तन को स्लाइसिंग के बजाय स्केलिंग विधि हस्ताक्षर का उपयोग करना था, है ना? किसी कारण से यह मेरे लिए काम नहीं कर रहा है।
तेलनौर

3

इस समाधान में से कुछ के साथ समस्या यह है कि वे सीधे पिक्सेल डेटा तक पहुंचते हैं और डाउनसमलिंग करने के लिए इसके माध्यम से लूप तक पहुंचते हैं। छवि के आकार के आधार पर यह बहुत ही संसाधन गहन हो सकता है, और ब्राउज़र के आंतरिक एल्गोरिदम का उपयोग करना बेहतर होगा।

DrawImage () समारोह एक रेखीय-प्रक्षेप उपयोग कर रहा है, निकटतम-पड़ोसी विधि resampling। जब आप आधे से अधिक मूल आकार से नीचे का आकार परिवर्तन नहीं कर रहे हैं तो यह अच्छी तरह से काम करता है

यदि आप एक बार में अधिकतम एक आधा का आकार बदलने के लिए लूप करते हैं, तो परिणाम काफी अच्छे होंगे, और पिक्सेल डेटा तक पहुंचने की तुलना में बहुत तेज होगा।

यह फ़ंक्शन वांछित आकार तक पहुंचने तक एक समय में आधा हो जाता है:

  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 का उपयोग करते हैं, तो यह वास्तव में कैनवास के फुल कैनवस को बढ़ाने या घटने के बजाय पूर्ण कैनवास को फिट करने के लिए (अर्थात: आकार बदलने) करेगा।

यह छवि को आकार के लिए निर्धारित ऊंचाई और चौड़ाई विशेषताओं के साथ एक कैनवास नियंत्रण में चित्र बनाने की कोशिश करने के लिए एक शॉट के लायक होगा और फिर सीएसएस का उपयोग करके कैनवास को उस आकार में आकार दें, जिसकी आप तलाश कर रहे हैं। शायद यह एक अलग आकार बदलने वाले एल्गोरिदम का उपयोग करेगा।

यह भी ध्यान दिया जाना चाहिए कि विभिन्न ब्राउज़रों में कैनवास के अलग-अलग प्रभाव होते हैं (और अलग-अलग ब्राउज़र के अलग-अलग संस्करण)। ब्राउज़रों में उपयोग किए जाने वाले एल्गोरिदम और तकनीक समय के साथ बदलने की संभावना है (विशेष रूप से फ़ायरफ़ॉक्स 4 और क्रोम 6 इतनी जल्दी बाहर आने के साथ, जो कैनवास रेंडरिंग प्रदर्शन पर भारी जोर देगा)।

इसके अलावा, आप एसवीजी को एक शॉट देना चाह सकते हैं, वह भी, क्योंकि यह एक अलग एल्गोरिथ्म का भी उपयोग करता है।

शुभकामनाएँ!


1
HTML विशेषताओं के माध्यम से एक कैनवास की चौड़ाई या ऊंचाई निर्धारित करने से कैनवास साफ हो जाता है, इसलिए उस विधि से कोई भी आकार परिवर्तन नहीं किया जा सकता है। इसके अलावा, एसवीजी गणितीय छवियों से निपटने के लिए है। मुझे पीएनजी और इस तरह से आकर्षित करने में सक्षम होने की जरूरत है, ताकि वहां मेरी मदद न करें।
तेलनूर

कैनवास की ऊँचाई और चौड़ाई सेट करना और CSS का उपयोग करने का आकार बदलने में मदद नहीं करता है, मैंने पाया है (क्रोम में)। यहां तक ​​कि सीएसएस चौड़ाई / ऊंचाई के बजाय -webkit- परिवर्तन का उपयोग करते हुए भी अंतर प्रक्षेप जा रहा नहीं है।
नेस्टर

2

मुझे लग रहा है कि मैंने जो मॉड्यूल लिखा है वह फ़ोटोशॉप के समान परिणाम देगा, क्योंकि यह एल्गोरिदम को लागू नहीं करते हुए औसत रूप से रंगीन डेटा को संरक्षित करता है। यह धीमा है, लेकिन मेरे लिए यह सबसे अच्छा है, क्योंकि यह सभी रंग डेटा को संरक्षित करता है।

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

यह निकटतम पड़ोसी को नहीं लेता है और अन्य पिक्सल को छोड़ देता है, या एक समूह का नमूना लेता है और एक यादृच्छिक औसत लेता है। यह सटीक अनुपात लेता है प्रत्येक स्रोत पिक्सेल को गंतव्य पिक्सेल में आउटपुट करना चाहिए। स्रोत में औसत पिक्सेल रंग गंतव्य में औसत पिक्सेल रंग होगा, जो कि ये अन्य सूत्र हैं, मुझे लगता है कि वे नहीं होंगे।

https://github.com/danschumann/limby-resize के निचले भाग में उपयोग करने का एक उदाहरण है

UPDATE OCT 2018 : इन दिनों मेरा उदाहरण और कुछ की तुलना में अधिक अकादमिक है। वेबलग बहुत अधिक 100% है, इसलिए आप इसी तरह के परिणाम उत्पन्न करने के लिए बेहतर होगा, लेकिन तेजी से। PICA.js ऐसा करता है, मेरा मानना ​​है। -


1

मैंने @ syockit के उत्तर के साथ-साथ किसी भी रुचि रखने वाले के लिए पुन: प्रयोज्य कोणीय सेवा में चरणबद्ध दृष्टिकोण को परिवर्तित किया: https://gist.github.com/fisch0920/37bac5e741eaec60e983

मैंने दोनों समाधानों को शामिल किया क्योंकि दोनों के अपने-अपने पक्ष / विपक्ष हैं। लैंकोस कनवल्शन एप्रोच धीमा होने की कीमत पर उच्च गुणवत्ता वाला है, जबकि स्टेप-वाइज डाउनस्कूलिंग दृष्टिकोण यथोचित एंटीएलियास परिणाम पैदा करता है और काफी तेज है।

उदाहरण का उपयोग:

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

तेज और सरल जावास्क्रिप्ट छवि Resizer:

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.
})

इतिहास

यह वास्तव में अनुसंधान, पढ़ने और कोशिश करने के कई दौर के बाद है।

Resizer एल्गोरिथ्म @ ViliusL की Hermite स्क्रिप्ट का उपयोग करता है (Hermite resizer वास्तव में सबसे तेज़ है और यथोचित अच्छा आउटपुट देता है)। आपके द्वारा आवश्यक सुविधाओं के साथ विस्तारित।

1 वर्कर को रीसाइज़िंग करने के लिए फोर्स करता है ताकि यह आपके ब्राउजर को फ्रीज करते समय फ्रीज न करे, अन्य सभी जेएस रिसैजर्स के विपरीत।


0

एक शानदार जवाब के लिए @syockit धन्यवाद। हालाँकि, मुझे इसे सुधारने के लिए थोड़ा सुधार करना पड़ा। डोम स्कैनिंग मुद्दों के कारण शायद:

$(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

मैं बस पक्षों की तुलना करके पृष्ठ का एक हिस्सा चलाता हूं और जब तक कि हाल ही में कुछ नहीं बदला है, मैं कैनवास बनाम सरल सीएसएस का उपयोग करके कोई बेहतर डाउनसाइजिंग (स्केलिंग) नहीं देख सकता था। मैंने एफएफ 6 मैक ओएसएक्स 10.7 में परीक्षण किया। अभी भी मूल बनाम थोड़ा नरम है।

लेकिन मैंने उस चीज़ पर ठोकर खाई, जिससे बहुत फर्क पड़ा और वह कैनवास का समर्थन करने वाले ब्राउज़रों में छवि फ़िल्टर का उपयोग कर रही थी। आप वास्तव में फ़ोटो में बहुत हेरफेर कर सकते हैं जैसे आप फ़ोटोशॉप में ब्लर, शार्पन, सैचुरेशन, रिपल, ग्रेस्केल आदि के साथ कर सकते हैं।

मुझे तब एक भयानक jQuery प्लग-इन मिला, जिसमें इन फिल्टरों का अनुप्रयोग एक स्नैप करता है: http://codecanyon.net/item/jsmanipulate-jquery-image-manipulation-plugin/428234

मैं बस उस छवि को आकार देने के ठीक बाद पैनापन फ़िल्टर लागू करता हूं जो आपको वांछित प्रभाव देना चाहिए। मैं भी एक कैनवास तत्व का उपयोग करने के लिए नहीं था।


-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> Jesus Carrera का उपयोग क्यों नहीं कर सकते?
ale500
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.