فلاتر الصور باستخدام اللوحة

مقدمة

يمكن استخدام عنصر لوحة 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;
};

في ما يلي فلتر تضخيم بدرجة 3×3. لاحظ كيف تركّز القيمة على البكسل المركزي. للحفاظ على سطوع الصورة، يجب أن يكون مجموع قيم المصفوفة واحدًا.

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 التدرّجات العمودية والأفقية للصورة ويجمع الصور المحسوبة لإيجاد الحواف في الصورة. تتمّ طريقة تطبيق فلتر Sobel هنا من خلال ضبط الصورة على درجات الرمادي أولاً، ثمّ أخذ التدرجات الأفقية والرأسية، وأخيراً دمج الصور المتدرّجة لإنشاء الصورة النهائية.

في ما يتعلق بالمصطلحات، يشير "التدرّج" هنا إلى التغيُّر في قيمة البكسل في موضع الصورة. إذا كان لأحد وحدات البكسل جارًا على يمينه قيمته ‏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 باستخدام علامة لوحة HTML. أنصحك باستخدام المزيد من فلاتر الصور، إنها ممتعة جدًا!

إذا كنت تحتاج إلى أداء أفضل من الفلاتر، يمكنك عادةً نقلها لاستخدام أدوات تظليل أجزاء WebGL لإجراء معالجة الصور. تتيح لك أدوات التظليل تشغيل معظم الفلاتر البسيطة في الوقت الفعلي، ما يتيح لك استخدامها بعد معالجة الفيديوهات والرسوم المتحركة.