जावास्क्रिप्ट कैनवास के साथ छवि का आकार बदलें (सुचारू रूप से)


90

मैं कैनवास के साथ कुछ छवियों का आकार बदलने की कोशिश कर रहा हूं, लेकिन मैं उन्हें कैसे चिकना करना चाहता हूं, इस पर मैं स्पष्ट हूं। फ़ोटोशॉप, ब्राउज़र आदि पर। कुछ एल्गोरिदम हैं जिनका वे उपयोग करते हैं (जैसे बाइसिक, बिलिनियर) लेकिन मुझे नहीं पता कि ये कैनवास में बने हैं या नहीं।

यहाँ मेरी बेला है: http://jsfiddle.net/EWupT/

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width=300
canvas.height=234
ctx.drawImage(img, 0, 0, 300, 234);
document.body.appendChild(canvas);

पहला एक सामान्य आकार का छवि टैग है, और दूसरा एक कैनवास है। ध्यान दें कि कैनवास एक जैसा चिकना नहीं है। मैं 'चिकनाई' कैसे प्राप्त कर सकता हूं?

जवाबों:


136

आप बेहतर परिणाम प्राप्त करने के लिए डाउन-स्टेपिंग का उपयोग कर सकते हैं। अधिकांश ब्राउज़र छवियों का आकार बदलते समय द्वि-घन के बजाय रैखिक प्रक्षेप का उपयोग करते हैं।

( अपडेट में ऐनक के लिए एक गुणवत्ता गुण जोड़ा गया है, imageSmoothingQualityजो वर्तमान में केवल क्रोम में उपलब्ध है।)

जब तक कोई बिना किसी चौरसाई या निकटतम पड़ोसी का चयन नहीं करता है तब तक ब्राउज़र हमेशा छवि को नीचे-स्केल करने के बाद प्रक्षेपित करेगा क्योंकि यह फ़ंक्शन अलियासिंग से बचने के लिए कम-पास फिल्टर के रूप में कार्य करता है।

द्वि-रेखीय 2x2 पिक्सेल का उपयोग प्रक्षेप करने के लिए करता है जबकि द्वि-घन 4x4 का उपयोग करता है इसलिए इसे चरणों में करके आप द्वि-रेखीय प्रक्षेप के करीब प्राप्त कर सकते हैं जबकि परिणामस्वरूप चित्र में देखा जा सकता है।

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

img.onload = function () {

    // set size proportional to image
    canvas.height = canvas.width * (img.height / img.width);

    // step 1 - resize to 50%
    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);

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

    // step 3, resize to final size
    ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
    0, 0, canvas.width, canvas.height);
}
img.src = "//i.imgur.com/SHo6Fub.jpg";
<img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234">
<canvas id="canvas" width=300></canvas>

यदि आपका अंतर कम है, तो आप इस बात पर निर्भर करते हैं कि अंतर कम होने पर आप चरण 2 को छोड़ सकते हैं।

डेमो में आप देख सकते हैं कि नया परिणाम अब छवि तत्व के समान है।


1
@ हेव, कभी-कभी ये बातें होती हैं :) छवियों के लिए आप आमतौर पर एक css BTW सेट करके इसे ओवरराइड कर सकते हैं।

केन, पहले परिणाम ने बहुत अच्छा काम किया, लेकिन जब मैं छवियों को बदलता हूं, तो आप देख सकते हैं कि यह बहुत धुंधला हो रहा है jsfiddle.net/kcHLG इस मामले में और अन्य क्या किया जा सकता है?
स्टीव

@ जल्दबाज़ी में आप चरणों की संख्या घटाकर सिर्फ 1 या कोई नहीं कर सकते हैं (कुछ छवियों के लिए यह ठीक काम करता है)। इस उत्तर को भी देखें जो इस के समान है लेकिन यहां मैंने इसमें एक तीक्ष्ण कनवल्शन जोड़ा है ताकि आप इसे डाउनस्कूल किए जाने के बाद परिणामी छवि को तेज बना सकें।

1
@steve यहाँ केवल एक अतिरिक्त कदम का उपयोग करके बिल के साथ एक संशोधित फ़िडेल है: jsfiddle.net/AbdiasSoftware/kcHLG/1

1
@neaumusic कोड OPs कोड की निरंतरता है। यदि आप फिडेल खोलते हैं तो आप देखेंगे कि ctx को परिभाषित किया जा रहा है। मैंने इसे गलतफहमी से बचने के लिए यहाँ रेखांकित किया है।

27

चूंकि ट्रुंग ले न्गुयेन न्हाट की बेला बिल्कुल भी सही नहीं है (यह मूल छवि का उपयोग अंतिम चरण में करता है)
मैंने प्रदर्शन की तुलना के साथ अपना स्वयं का सामान्य फिडेल लिखा:

बेला

मूल रूप से यह है:

img.onload = function() {
   var canvas = document.createElement('canvas'),
       ctx = canvas.getContext("2d"),
       oc = document.createElement('canvas'),
       octx = oc.getContext('2d');

   canvas.width = width; // destination canvas size
   canvas.height = canvas.width * img.height / img.width;

   var cur = {
     width: Math.floor(img.width * 0.5),
     height: Math.floor(img.height * 0.5)
   }

   oc.width = cur.width;
   oc.height = cur.height;

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

   while (cur.width * 0.5 > width) {
     cur = {
       width: Math.floor(cur.width * 0.5),
       height: Math.floor(cur.height * 0.5)
     };
     octx.drawImage(oc, 0, 0, cur.width * 2, cur.height * 2, 0, 0, cur.width, cur.height);
   }

   ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height);
}

अब तक के सबसे अंडररेटेड जवाब।
अनमस्कन्ना

17

मैंने किसी ऐसे व्यक्ति के लिए छवियों / कैनवस के उच्च गुणवत्ता वाले आकार को संभालने के लिए एक पुन: प्रयोज्य कोणीय सेवा बनाई है, जो किसी के लिए भी इच्छुक है: https://gist.github.com/transitive-bullshit/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
    })
})

इस बारे में क्षमा करें - मैंने अपना github उपयोगकर्ता नाम बदल दिया था। बस लिंक gist.github.com/transitive-bullshit/37bac5e741eaec60e983
fisch2

3
मैंने कोणीय शब्द देखा, मुझे वह अजीब लग रहा था
SuperUberDuper

8

हालांकि उन कोड-स्निपेट में से कुछ कम और काम कर रहे हैं, वे अनुसरण करने और समझने के लिए तुच्छ नहीं हैं।

जैसा कि मैं स्टैक-ओवरफ्लो से "कॉपी-पेस्ट" का प्रशंसक नहीं हूं, मैं डेवलपर्स को कोड को समझना चाहूंगा कि वे उस सॉफ़्टवेयर में धक्का दे रहे हैं, आशा है कि आप नीचे उपयोगी पाएंगे।

डेमो : जेएस और एचटीएमएल कैनवस डेमो फिडलर के साथ छवियों का आकार बदलना।

यह आकार बदलने के लिए आपको 3 अलग-अलग तरीके मिल सकते हैं, जिससे आपको यह समझने में मदद मिलेगी कि कोड कैसे काम कर रहा है और क्यों।

https://jsfiddle.net/1b68eLdr/93089/

दोनों डेमो का पूरा कोड, और टाइपस्क्रिप्ट विधि जिसे आप अपने कोड में उपयोग करना चाहते हैं, GitHub प्रोजेक्ट में पाया जा सकता है।

https://github.com/eyalc4/ts-image-resizer

यह अंतिम कोड है:

export class ImageTools {
base64ResizedImage: string = null;

constructor() {
}

ResizeImage(base64image: string, width: number = 1080, height: number = 1080) {
    let img = new Image();
    img.src = base64image;

    img.onload = () => {

        // Check if the image require resize at all
        if(img.height <= height && img.width <= width) {
            this.base64ResizedImage = base64image;

            // TODO: Call method to do something with the resize image
        }
        else {
            // Make sure the width and height preserve the original aspect ratio and adjust if needed
            if(img.height > img.width) {
                width = Math.floor(height * (img.width / img.height));
            }
            else {
                height = Math.floor(width * (img.height / img.width));
            }

            let resizingCanvas: HTMLCanvasElement = document.createElement('canvas');
            let resizingCanvasContext = resizingCanvas.getContext("2d");

            // Start with original image size
            resizingCanvas.width = img.width;
            resizingCanvas.height = img.height;


            // Draw the original image on the (temp) resizing canvas
            resizingCanvasContext.drawImage(img, 0, 0, resizingCanvas.width, resizingCanvas.height);

            let curImageDimensions = {
                width: Math.floor(img.width),
                height: Math.floor(img.height)
            };

            let halfImageDimensions = {
                width: null,
                height: null
            };

            // Quickly reduce the dize by 50% each time in few iterations until the size is less then
            // 2x time the target size - the motivation for it, is to reduce the aliasing that would have been
            // created with direct reduction of very big image to small image
            while (curImageDimensions.width * 0.5 > width) {
                // Reduce the resizing canvas by half and refresh the image
                halfImageDimensions.width = Math.floor(curImageDimensions.width * 0.5);
                halfImageDimensions.height = Math.floor(curImageDimensions.height * 0.5);

                resizingCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height,
                    0, 0, halfImageDimensions.width, halfImageDimensions.height);

                curImageDimensions.width = halfImageDimensions.width;
                curImageDimensions.height = halfImageDimensions.height;
            }

            // Now do final resize for the resizingCanvas to meet the dimension requirments
            // directly to the output canvas, that will output the final image
            let outputCanvas: HTMLCanvasElement = document.createElement('canvas');
            let outputCanvasContext = outputCanvas.getContext("2d");

            outputCanvas.width = width;
            outputCanvas.height = height;

            outputCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height,
                0, 0, width, height);

            // output the canvas pixels as an image. params: format, quality
            this.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85);

            // TODO: Call method to do something with the resize image
        }
    };
}}

4

मैंने एक पुस्तकालय बनाया जो आपको सभी रंग डेटा रखते हुए किसी भी प्रतिशत को कम करने की अनुमति देता है।

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

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


1
मैं शायद अब आकार बदलने के लिए
वेबलॉग का

4

K3N उत्तर के आधार पर, मैं आमतौर पर किसी को भी चाहता हूं के लिए कोड को फिर से लिखना

var oc = document.createElement('canvas'), octx = oc.getContext('2d');
    oc.width = img.width;
    oc.height = img.height;
    octx.drawImage(img, 0, 0);
    while (oc.width * 0.5 > width) {
       oc.width *= 0.5;
       oc.height *= 0.5;
       octx.drawImage(oc, 0, 0, oc.width, oc.height);
    }
    oc.width = width;
    oc.height = oc.width * img.height / img.width;
    octx.drawImage(img, 0, 0, oc.width, oc.height);

अद्यतन JSF डेमो

यहाँ मेरा ऑनलाइन डेमो है


2
यह काम नहीं करेगा: हर बार जब आप कैनवास का आकार बदलते हैं, तो यह उसके संदर्भ को साफ कर देगा। आपको 2 कैनवस चाहिए। यहाँ यह सीधे अंतिम आयामों के साथ सीधे आह्वान के रूप में ही है।
काइदो

2

मुझे समझ नहीं आता कि कोई सुझाव क्यों नहीं दे रहा है createImageBitmap

createImageBitmap(
    document.getElementById('image'), 
    { resizeWidth: 300, resizeHeight: 234, resizeQuality: 'high' }
)
.then(imageBitmap => 
    document.getElementById('canvas').getContext('2d').drawImage(imageBitmap, 0, 0)
);

खूबसूरती से काम करता है (आप छवि और कैनवास के लिए आईडी सेट करते हैं)।


यह व्यापक रूप से समर्थित नहीं है क्योंकि caniuse.com/#search=createImageBitmap
मैट

createImageBitmap सभी उपयोगकर्ताओं के 73% के लिए समर्थित है। आपके उपयोग के मामले के आधार पर, यह काफी अच्छा हो सकता है। यह सिर्फ सफारी है जो इसके लिए समर्थन जोड़ने से इनकार करती है। मुझे लगता है कि यह संभव समाधान के रूप में ध्यान देने योग्य है।
cagdas_ucar

अच्छा समाधान है, लेकिन यह दुर्भाग्य से फ़ायरफ़ॉक्स पर काम नहीं करता है
vcarel

1

मैंने फसल के लिए छोटे js- उपयोगिता लिखी और फ्रंट-एंड पर छवि का आकार बदला। यहाँ GitHub परियोजना पर लिंक है। इसके अलावा आप इसे भेजने के लिए अंतिम छवि से ब्लॉब प्राप्त कर सकते हैं।

import imageSqResizer from './image-square-resizer.js'

let resizer = new imageSqResizer(
    'image-input',
    300,
    (dataUrl) => 
        document.getElementById('image-output').src = dataUrl;
);
//Get blob
let formData = new FormData();
formData.append('files[0]', resizer.blob);

//get dataUrl
document.getElementById('image-output').src = resizer.dataUrl;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.