कैनवस के साथ इमेज फ़िल्टर

इल्मारी हीकिनेन

शुरुआती जानकारी

HTML5 कैनवस एलिमेंट का इस्तेमाल, इमेज फ़िल्टर लिखने के लिए किया जा सकता है. इसके लिए आपको कैनवस पर इमेज बनानी होगी, कैनवस पिक्सल को फिर से पढ़ना होगा और उन पर फ़िल्टर चलाना होगा. इसके बाद, आप इस नतीजे को एक नए कैनवस पर लिख सकते हैं (या मोटे तौर पर, सिर्फ़ पुराने कैनवस का फिर से इस्तेमाल करें.)

आसान लगता है? बढ़िया. चलिए काम शुरू करें!

ओरिजनल टेस्ट इमेज
ओरिजनल टेस्ट इमेज

पिक्सल प्रोसेस किए जा रहे हैं

सबसे पहले, इमेज के पिक्सल फिर से पाएं:

Filters = {};
Filters.getPixels = function(img) {
var c = this.getCanvas(img.width, img.height);
var ctx = c.getContext('2d');
ctx.drawImage(img);
return ctx.getImageData(0,0,c.width,c.height);
};

Filters.getCanvas = function(w,h) {
var c = document.createElement('canvas');
c.width = w;
c.height = h;
return c;
};

इसके बाद, हमें इमेज को फ़िल्टर करने का एक तरीका चाहिए. क्या filterImage का कोई ऐसा तरीका कैसा है जो फ़िल्टर और इमेज लेकर, फ़िल्टर किए गए पिक्सल दिखाता है?

Filters.filterImage = function(filter, image, var_args) {
var args = [this.getPixels(image)];
for (var i=2; i<arguments.length; i++) {
args.push(arguments[i]);
}
return filter.apply(null, args);
};

आसान फ़िल्टर चलाना

अब जब हमने पिक्सल प्रोसेसिंग पाइपलाइन को जोड़ लिया है, तो कुछ आसान फ़िल्टर लिखने का समय आ गया है. शुरू करने के लिए, आइए इमेज को ग्रेस्केल में बदलें.

Filters.grayscale = function(pixels, args) {
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
var r = d[i];
var g = d[i+1];
var b = d[i+2];
// CIE luminance for the RGB
// The human eye is bad at seeing red and blue, so we de-emphasize them.
var v = 0.2126*r + 0.7152*g + 0.0722*b;
d[i] = d[i+1] = d[i+2] = v
}
return pixels;
};

पिक्सल में एक तय वैल्यू जोड़कर, चमक में बदलाव किया जा सकता है:

Filters.brightness = function(pixels, adjustment) {
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
d[i] += adjustment;
d[i+1] += adjustment;
d[i+2] += adjustment;
}
return pixels;
};

इमेज को थ्रेशहोल्ड करना भी काफ़ी आसान है. आपको सिर्फ़ पिक्सल के ग्रेस्केल वैल्यू की तुलना थ्रेशोल्ड वैल्यू से करनी होती है और उसके आधार पर कलर सेट करना होता है:

Filters.threshold = function(pixels, threshold) {
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
var r = d[i];
var g = d[i+1];
var b = d[i+2];
var v = (0.2126*r + 0.7152*g + 0.0722*b >= threshold) ? 255 : 0;
d[i] = d[i+1] = d[i+2] = v
}
return pixels;
};

मिलती-जुलती इमेज

कन्वर्ज़न फ़िल्टर, इमेज प्रोसेसिंग के लिए बहुत काम के सामान्य फ़िल्टर होते हैं. बुनियादी तरीका यह है कि आप सोर्स इमेज से पिक्सल के आयत का मापा गया योग निकालें और उसे आउटपुट वैल्यू के तौर पर इस्तेमाल करें. कॉन्वोल्यूशन फ़िल्टर का इस्तेमाल धुंधला करने, शार्प करने, एंबॉसिंग, किनारे का पता लगाने और कई दूसरी चीज़ों के लिए किया जा सकता है.

Filters.tmpCanvas = document.createElement('canvas');
Filters.tmpCtx = Filters.tmpCanvas.getContext('2d');

Filters.createImageData = function(w,h) {
return this.tmpCtx.createImageData(w,h);
};

Filters.convolute = function(pixels, weights, opaque) {
var side = Math.round(Math.sqrt(weights.length));
var halfSide = Math.floor(side/2);
var src = pixels.data;
var sw = pixels.width;
var sh = pixels.height;
// pad output by the convolution matrix
var w = sw;
var h = sh;
var output = Filters.createImageData(w, h);
var dst = output.data;
// go through the destination image pixels
var alphaFac = opaque ? 1 : 0;
for (var y=0; y<h; y++) {
for (var x=0; x<w; x++) {
  var sy = y;
  var sx = x;
  var dstOff = (y*w+x)*4;
  // calculate the weighed sum of the source image pixels that
  // fall under the convolution matrix
  var r=0, g=0, b=0, a=0;
  for (var cy=0; cy<side; cy++) {
    for (var cx=0; cx<side; cx++) {
      var scy = sy + cy - halfSide;
      var scx = sx + cx - halfSide;
      if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
        var srcOff = (scy*sw+scx)*4;
        var wt = weights[cy*side+cx];
        r += src[srcOff] * wt;
        g += src[srcOff+1] * wt;
        b += src[srcOff+2] * wt;
        a += src[srcOff+3] * wt;
      }
    }
  }
  dst[dstOff] = r;
  dst[dstOff+1] = g;
  dst[dstOff+2] = b;
  dst[dstOff+3] = a + alphaFac*(255-a);
}
}
return output;
};

पेश है 3x3 आसपेक्ट रेशियो वाला फ़िल्टर. देखें कि यह सेंटर पिक्सल पर वज़न को कैसे फ़ोकस करता है. इमेज की चमक बनाए रखने के लिए, मैट्रिक्स वैल्यू का योग एक होना चाहिए.

Filters.filterImage(Filters.convolute, image,
[  0, -1,  0,
-1,  5, -1,
  0, -1,  0 ]
);

यहां कॉन्वलूशन फ़िल्टर का एक और उदाहरण दिया गया है, यानी बॉक्स को धुंधला किया गया. बॉक्स ब्लर, कन्वलूशन मैट्रिक्स के अंदर पिक्सल वैल्यू का औसत दिखाता है. इसका तरीका यह है कि NxN साइज़ का कॉन्वलूशन मैट्रिक्स बनाएं, जहां हर वेट 1 / (NxN) है. इस तरह मैट्रिक्स के अंदर मौजूद हर पिक्सल, आउटपुट इमेज में बराबर संख्या में योगदान देता है और वैल्यू का कुल योग एक होता है.

Filters.filterImage(Filters.convolute, image,
[ 1/9, 1/9, 1/9,
1/9, 1/9, 1/9,
1/9, 1/9, 1/9 ]
);

हम मौजूदा फ़िल्टर को मिलाकर और ज़्यादा जटिल इमेज फ़िल्टर बना सकते हैं. उदाहरण के लिए, आइए एक Sobel फ़िल्टर लिखते हैं. सोबेल फ़िल्टर, इमेज के वर्टिकल और हॉरिज़ॉन्टल ग्रेडिएंट का पता लगाता है. साथ ही, इमेज के किनारों को ढूंढने के लिए, कंप्यूट किए गए इमेज को जोड़ता है. हम यहां Sobel फ़िल्टर का इस्तेमाल करते हैं. इसके लिए, पहले इमेज को ग्रेस्केल करना होता है, फिर हॉरिज़ॉन्टल और वर्टिकल ग्रेडिएंट लेना और आखिर में ग्रेडिएंट इमेज को मिलाकर फ़ाइनल इमेज बनानी होती है.

शब्दावली में, यहां "ग्रेडिएंट" का मतलब किसी इमेज के क्रम पर पिक्सल वैल्यू में होने वाले बदलाव से है. अगर किसी Pixel का बायां पड़ोसी मान 20 है और दायां पड़ोसी मान 50 है, तो पिक्सल पर हॉरिज़ॉन्टल ग्रेडिएंट 30 होगा. वर्टिकल ग्रेडिएंट का आइडिया एक जैसा है, लेकिन ऊपर और नीचे वाले पड़ोसियों का इस्तेमाल करता है.

var grayscale = Filters.filterImage(Filter.grayscale, image);
// Note that ImageData values are clamped between 0 and 255, so we need
// to use a Float32Array for the gradient values because they
// range between -255 and 255.
var vertical = Filters.convoluteFloat32(grayscale,
[ -1, 0, 1,
-2, 0, 2,
-1, 0, 1 ]);
var horizontal = Filters.convoluteFloat32(grayscale,
[ -1, -2, -1,
  0,  0,  0,
  1,  2,  1 ]);
var final_image = Filters.createImageData(vertical.width, vertical.height);
for (var i=0; i<final_image.data.length; i+=4) {
// make the vertical gradient red
var v = Math.abs(vertical.data[i]);
final_image.data[i] = v;
// make the horizontal gradient green
var h = Math.abs(horizontal.data[i]);
final_image.data[i+1] = h;
// and mix in some blue for aesthetics
final_image.data[i+2] = (v+h)/4;
final_image.data[i+3] = 255; // opaque alpha
}

इसके अलावा, कॉन्वलूशन फ़िल्टर के कई और फ़िल्टर मौजूद हैं, जो आपके लिए इंतज़ार करते रहते हैं. उदाहरण के लिए, ऊपर दिए गए कॉन्वलूशन टॉय में लाप्लास फ़िल्टर लगाएं और देखें कि इससे क्या होता है.

नतीजा

मुझे उम्मीद है कि इस छोटे से लेख से, एचटीएमएल कैनवस टैग का इस्तेमाल करके, JavaScript में इमेज फ़िल्टर लिखने की बुनियादी बातों को समझाने में मदद मिलेगी. मेरी सलाह है कि आप कुछ और इमेज फ़िल्टर लागू करें. यह काफ़ी मज़ेदार है!

अगर आपको अपने फ़िल्टर से बेहतर परफ़ॉर्मेंस की ज़रूरत है, तो आम तौर पर इमेज प्रोसेसिंग के लिए, WebGL फ़्रैगमेंट शेडर का इस्तेमाल करके उन्हें पोर्ट किया जा सकता है. शेडर की मदद से, रीयल टाइम में सबसे आसान फ़िल्टर चलाए जा सकते हैं. इनसे आपको पोस्ट-प्रोसेस वीडियो और ऐनिमेशन के लिए फ़िल्टर इस्तेमाल करने की सुविधा मिलती है.