Html5 कैनवस ड्रॉइमेज: एंटीलियासिंग कैसे लागू करें


82

कृपया निम्नलिखित उदाहरण पर एक नज़र डालें:

http://jsfiddle.net/MLGr4/47/

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

img = new Image();
img.onload = function(){
    canvas.width = 400;
    canvas.height = 150;
    ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 400, 150);
}
img.src = "http://openwalls.com/image/1734/colored_lines_on_blue_background_1920x1200.jpg";

जैसा कि आप देखते हैं, छवि एंटी-अलियास नहीं है, हालांकि यह कहा जाता है कि ड्राइमेज स्वचालित रूप से एंटी अलियासिंग लागू करता है। मैंने कई तरह से कोशिश की लेकिन यह काम नहीं करता है। क्या आप मुझे बता सकते हैं कि मैं कैसे विरोधी अलियास छवि प्राप्त कर सकता हूं? धन्यवाद।

जवाबों:


178

वजह

कुछ छवियों को बस नमूने के लिए बहुत मुश्किल है और इस तरह के एक के रूप में प्रक्षेपित करें जब आप बड़े आकार से छोटे तक जाना चाहते हैं।

ब्राउज़र आमतौर पर (संभावित) प्रदर्शन कारणों के लिए द्वि-घन (4x4 नमूना) के बजाय कैनवास तत्व के साथ द्वि-रैखिक (2x2 नमूनाकरण) प्रक्षेप का उपयोग करते दिखाई देते हैं।

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

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

उपाय

अपडेट 2018:

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

त्रिज्या के रूप में चरणों की मूल संख्या (मूल आकार / गंतव्य आकार / 2) का उपयोग करके पूर्व-धुंधला (आपको ब्राउज़र और विषम / यहां तक ​​कि चरणों के आधार पर इसे व्यवस्थित रूप से समायोजित करने की आवश्यकता हो सकती है - यहां केवल सरलीकृत दिखाया गया है):

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

if (typeof ctx.filter === "undefined") {
 alert("Sorry, the browser doesn't support Context2D filters.")
}

const img = new Image;
img.onload = function() {

  // step 1
  const oc = document.createElement('canvas');
  const octx = oc.getContext('2d');
  oc.width = this.width;
  oc.height = this.height;

  // steo 2: pre-filter image using steps as radius
  const steps = (oc.width / canvas.width)>>1;
  octx.filter = `blur(${steps}px)`;
  octx.drawImage(this, 0, 0);

  // step 3, draw scaled
  ctx.drawImage(oc, 0, 0, oc.width, oc.height, 0, 0, canvas.width, canvas.height);

}
img.src = "//i.stack.imgur.com/cYfuM.jpg";
body{ background-color: ivory; }
canvas{border:1px solid red;}
<br/><p>Original was 1600x1200, reduced to 400x300 canvas</p><br/>
<canvas id="canvas" width=400 height=250></canvas>

फिल्टर के लिए समर्थन के रूप में ogf Oct / 2018:

अद्यतन 2017: अब एक नया गुण है जो गुणवत्ता को फिर से स्थापित करने के लिए ऐनक में परिभाषित किया गया है:

context.imageSmoothingQuality = "low|medium|high"

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

इसके लिए समर्थन imageSmoothingQuality:

ब्राउज़र। तब तक ..:
ट्रांसमिशन का अंत

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

यह द्वि-रैखिक प्रक्षेप के साथ अच्छे परिणाम भी देगा (यह वास्तव में ऐसा करते समय द्वि-घन की तरह व्यवहार करता है) और ओवरहेड न्यूनतम है क्योंकि प्रत्येक चरण में नमूना करने के लिए कम पिक्सेल हैं।

आदर्श चरण प्रत्येक चरण में आधे रिज़ॉल्यूशन पर जाना है जब तक कि आप लक्ष्य आकार निर्धारित नहीं करेंगे (इस बात का उल्लेख करने के लिए जो मेबेल के लिए धन्यवाद!)।

संशोधित बेला

मूल प्रश्न के रूप में प्रत्यक्ष स्केलिंग का उपयोग करना:

नोर्मल डॉक-स्कैलड इमेज

नीचे दिखाए गए अनुसार स्टेप-डाउन का उपयोग करना:

नीचे-नीचे छवि

इस स्थिति में आपको 3 चरणों में नीचे जाने की आवश्यकता होगी:

चरण 1 में हम ऑफ-स्क्रीन कैनवास का उपयोग करके छवि को आधा कर देते हैं:

// step 1 - create off-screen canvas
var oc   = document.createElement('canvas'),
    octx = oc.getContext('2d');

oc.width  = img.width  * 0.5;
oc.height = img.height * 0.5;

octx.drawImage(img, 0, 0, oc.width, oc.height);

चरण 2 ऑफ-स्क्रीन कैनवास का पुन: उपयोग करता है और कम की गई छवि को फिर से आधे तक खींचता है:

// step 2
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);

और हम मुख्य कैनवास पर एक बार फिर से आकर्षित होते हैं, फिर से घटकर आधा हो जाता है , लेकिन अंतिम आकार में:

// step 3
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
                  0, 0, canvas.width,   canvas.height);

टिप:

आप इस सूत्र का उपयोग करके आवश्यक कुल चरणों की गणना कर सकते हैं (इसमें लक्ष्य आकार निर्धारित करने के लिए अंतिम चरण शामिल है):

steps = Math.ceil(Math.log(sourceWidth / targetWidth) / Math.log(2))

4
कुछ बहुत बड़ी प्रारंभिक छवियों (8000 x 6000 और ऊपर की ओर) के साथ काम करते हुए मुझे मूल रूप से चरण 2 में तब तक उपयोगी लगता है जब तक मुझे वांछित आकार के 2 के एक कारक के भीतर नहीं मिलता।
जो मैबेल

एक जादू की तरह काम करता है! थॅंक्स!
व्लाद त्सेप्लेव

1
मैं दूसरे और तीसरे चरण के बीच के अंतर पर भ्रमित हूं ... क्या कोई समझा सकता है?
carinlynchin

1
@ कैराइन यह थोड़ा जटिल है, लेकिन कैनवस जितनी जल्दी हो सके एक पिंग को बाहर निकालने की कोशिश करता है। Png फ़ाइल आंतरिक रूप से 5 अलग-अलग फ़िल्टर प्रकारों का समर्थन करती है जो कि संपीड़न (gzip) में सुधार कर सकती हैं, लेकिन सबसे अच्छा संयोजन खोजने के लिए इन सभी फ़िल्टर को छवि की प्रति पंक्ति पर परीक्षण करना होगा। यह बड़ी छवियों के लिए समय लेने वाला होगा और ब्राउज़र को ब्लॉक कर सकता है, इसलिए अधिकांश ब्राउज़र केवल फ़िल्टर 0 का उपयोग करते हैं और इसे कुछ संपीड़न प्राप्त करने की उम्मीद करते हुए धक्का देते हैं। आप इस प्रक्रिया को मैन्युअल रूप से कर सकते हैं लेकिन यह स्पष्ट रूप से थोड़ा अधिक काम है। या सेवा APIs जैसे कि tinypng.com के माध्यम से इसे चलाएं।

1
@ कैइडो इसे नहीं भूला है और "कॉपी" बहुत धीमा है। आपको स्पष्टता () का उपयोग करने के लिए और मुख्य या सर्वोच्च का उपयोग करने के लिए तेजी से पारदर्शिता की आवश्यकता है। लक्ष्य के रूप में कैनवास।

12

मैं ऐसे कार्यों के लिए पिका की अत्यधिक अनुशंसा करता हूं । इसकी गुणवत्ता कई डाउनसाइजिंग से बेहतर है और एक ही समय में काफी तेज है। यहाँ एक डेमो है


4
    var getBase64Image = function(img, quality) {
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;
    var ctx = canvas.getContext("2d");

    //----- origin draw ---
    ctx.drawImage(img, 0, 0, img.width, img.height);

    //------ reduced draw ---
    var canvas2 = document.createElement("canvas");
    canvas2.width = img.width * quality;
    canvas2.height = img.height * quality;
    var ctx2 = canvas2.getContext("2d");
    ctx2.drawImage(canvas, 0, 0, img.width * quality, img.height * quality);

    // -- back from reduced draw ---
    ctx.drawImage(canvas2, 0, 0, img.width, img.height);

    var dataURL = canvas.toDataURL("image/png");
    return dataURL;
    // return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}

1
पैरामीटर 'गुणवत्ता' का मान सीमा क्या है?
सेर

बीट्वीन जीरो और वन [0, 1]
Iván Rodríguez

4

केन के उत्तर के अलावा, यहाँ एक और हल हल में डाउनसमलिंग करने के लिए किया गया है (इसलिए परिणाम ब्राउज़र के एल्गोरिथ्म का उपयोग करके अच्छा दिखता है:

  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] );

3

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

जे एस:

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
   var url = "http://openwalls.com/image/17342/colored_lines_on_blue_background_1920x1200.jpg";

    img=new Image();
    img.onload=function(){

        canvas.style.backgroundImage = "url(\'" + url + "\')"

    }
    img.src="http://openwalls.com/image/17342/colored_lines_on_blue_background_1920x1200.jpg";

काम कर रहे डेमो


2

मैंने उन लोगों के लिए छवियों के उच्च गुणवत्ता वाले आकार को संभालने के लिए एक पुन: प्रयोज्य कोणीय सेवा बनाई है जो किसी की रुचि रखते हैं: 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
    })
})
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.