كيفية معالجة الصور في Node.js باستخدام Sharp

اختار الكاتب صندوق التنوع في التكنولوجيا لتلقي تبرع كجزء من برنامج الكتابة من أجل التبرعات.

المقدمة

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

لديك Node.js نظام من المكتبات التي يمكنك استخدامها لمعالجة الصور، مثل sharp, jimp, و gm module. ستركز هذه المقالة على وحدة الـ sharp. sharp هي مكتبة معالجة صور شهيرة في Node.js تدعم مختلف صيغ ملفات الصور، مثل JPEG, PNG, GIF, WebP, AVIF, SVG و TIFF. في هذا البرنامج التعليمي، ستستخدم sharp لقراءة صورة واستخراج بياناتها الوصفية، تغيير حجمها، تغيير تنسيق الصورة، وضغط الصورة. بعد ذلك، ستقوم بتقليمها، تحويلها لدرجة الرمادي، تدويرها، وضبابتها. في النهاية، ستقوم بتركيب الصور وإضافة نص على الصورة. في نهاية هذا البرنامج التعليمي، ستكتسب فهمًا جيدًا لكيفية معالجة الصور في Node.js.

المتطلبات المسبقة

لإكمال هذا البرنامج التعليمي، ستحتاج:

الخطوة 1 — إعداد مجلد المشروع وتحميل الصور

قبل البدء في كتابة الشفرة، تحتاج إلى إنشاء المجلد الذي سيحتوي على الشفرة والصور التي ستستخدمها في هذا المقال.

افتح الطرفية الخاصة بك وأنشئ المجلد للمشروع باستخدام أمر mkdir:

  1. mkdir process_images

انتقل إلى المجلد الذي تم إنشاؤه حديثًا باستخدام أمر cd:

  1. cd process_images

أنشئ ملف package.json باستخدام أمر npm init لتتبع تبعيات المشروع:

  1. npm init -y

الخيار -y يخبر npm بإنشاء ملف package.json الافتراضي.

ثم، قم بتثبيت sharp كتبعية:

  1. npm install sharp

ستستخدم الصور الثلاث التالية في هذا البرنامج التعليمي:



بعد ذلك، قم بتنزيل الصور في دليل مشروعك باستخدام أمر curl.

استخدم الأمر التالي لتنزيل الصورة الأولى. سيتم تنزيل الصورة بوصفها sammy.png:

  1. curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/sammy.png

بعد ذلك، قم بتنزيل الصورة الثانية باستخدام الأمر التالي. سيتم تنزيل الصورة بوصفها underwater.png:

  1. curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/underwater.png

وأخيرًا، قم بتنزيل الصورة الثالثة باستخدام الأمر التالي. سيتم تنزيل الصورة بوصفها sammy-transparent.png:

  1. curl -O https://assets.digitalocean.com/how-to-process-images-in-node-js-with-sharp/sammy-transparent.png

مع دليل المشروع والتبعيات معدة، أنت الآن جاهز لبدء معالجة الصور.

الخطوة ٢ — قراءة الصور وإخراج البيانات الوصفية

في هذا القسم، ستكتب الكود لقراءة صورة واستخراج البيانات الوصفية الخاصة بها. البيانات الوصفية للصورة هي نص مضمن في الصورة، وتشمل معلومات حول الصورة مثل نوعها وعرضها وارتفاعها.

لاستخراج البيانات الوصفية، ستقوم أولاً بتوريد وحدة الشارب، وإنشاء مثيل لـ sharp، ثم تمرير مسار الصورة كمعلمة. بعد ذلك، ستقوم بربط طريقة metadata() بالمثيل لاستخراج البيانات الوصفية وتسجيلها في وحدة التحكم.

للقيام بذلك، قم بإنشاء وفتح ملف readImage.js في محرر النص المفضل لديك. تستخدم هذه البرمجية التعليمية محرر نص سطر الأوامر يسمى nano:

  1. nano readImage.js

ثم، قم بطلب وحدة sharp في أعلى الملف:

process_images/readImage.js
const sharp = require("sharp");

شارب هو وحدة معالجة الصور القائمة على الوعد. عند إنشاء مثيل لـ sharp، يُرجع وعدًا. يمكنك حل الوعد باستخدام طريقة then أو استخدام async/await، الذي يتمتع بصيغة أنظف.

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

في ملفك readImage.js، قم بتعريف وظيفة غير متزامنة، getMetadata()، لقراءة الصورة، واستخراج بياناتها الوصفية، وتسجيلها في وحدة التحكم:

process_images/readImage.js
const sharp = require("sharp");

async function getMetadata() {
  const metadata = await sharp("sammy.png").metadata();
  console.log(metadata);
}

getMetadata() هي وظيفة غير متزامنة بناءً على الكلمة المفتاحية async التي قمت بتعريفها قبل التسمية function. هذا يتيح لك استخدام بناء الجملة await داخل الوظيفة. ستقوم وظيفة getMetadata() بقراءة صورة وإرجاع كائن يحتوي على بياناتها الوصفية.

Dentro del cuerpo de la función, lees la imagen llamando a sharp() lo cual toma la ruta de la imagen como argumento, aquí con sammy.png.

Además de tomar una ruta de imagen, sharp() también puede leer datos de imagen almacenados en un Buffer, Uint8Array o Uint8ClampedArray siempre que la imagen sea JPEG, PNG, GIF, WebP, AVIF, SVG o TIFF.

Ahora, cuando usas sharp() para leer la imagen, crea una instancia de sharp. Luego encadenas el método metadata() del módulo sharp a la instancia. El método devuelve un objeto que contiene los metadatos de la imagen, los cuales almacenas en la variable metadata y registras su contenido usando console.log().

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

للقيام بذلك، قم بتضمين الكود داخل دالة getMetadata() داخل كتلة try...catch:

process_images/readImage.js
const sharp = require("sharp");

async function getMetadata() {
  try {
    const metadata = await sharp("sammy.png").metadata();
    console.log(metadata);
  } catch (error) {
    console.log(`An error occurred during processing: ${error}`);
  }
}

داخل الكتلة try، قم بقراءة صورة، واستخراج وتسجيل بياناتها الوصفية. عند حدوث خطأ أثناء هذه العملية، يتخطى التنفيذ إلى القسم catch ويسجل الخطأ مما يمنع البرنامج من الانهيار.

أخيرًا، قم بإستدعاء دالة getMetadata() عن طريق إضافة السطر المحدد:

process_images/readImage.js

const sharp = require("sharp");

async function getMetadata() {
  try {
    const metadata = await sharp("sammy.png").metadata();
    console.log(metadata);
  } catch (error) {
    console.log(`An error occurred during processing: ${error}`);
  }
}

getMetadata();

الآن، قم بحفظ وخروج الملف. أدخل y لحفظ التغييرات التي قمت بها في الملف، وقم بتأكيد اسم الملف بالضغط على مفتاح ENTER أو RETURN.

قم بتشغيل الملف باستخدام أمر node:

  1. node readImage.js

يجب أن ترى نتيجة مشابهة لهذه:

Output
{ format: 'png', width: 750, height: 483, space: 'srgb', channels: 3, depth: 'uchar', density: 72, isProgressive: false, hasProfile: false, hasAlpha: false }

الآن بعد أن قمت بقراءة صورة واستخراج بياناتها الوصفية، ستقوم الآن بتغيير حجم الصورة وتغيير تنسيقها وضغطها.

الخطوة 3 — تغيير حجم الصورة، تغيير تنسيق الصورة، وضغط الصور

تغيير الحجم هو عملية تعديل أبعاد الصورة دون قطع أي شيء منها، مما يؤثر على حجم ملف الصورة. في هذا القسم، ستقوم بتغيير حجم الصورة وتغيير نوع الصورة، وضغط الصورة. ضغط الصورة هو عملية تقليل حجم ملف الصورة دون فقدان الجودة.

أولاً، ستقوم بربط الطريقة resize() من مثيل sharp لتغيير حجم الصورة، وحفظها في دليل المشروع. ثانياً، ستقوم بربط الطريقة format() إلى الصورة المُغيَّرَة الحجم لتغيير تنسيقها من png إلى jpeg. بالإضافة إلى ذلك، ستمرر خيارًا إلى الطريقة format() لضغط الصورة وحفظها في الدليل.

أنشئ وافتح ملف resizeImage.js في محرر النصوص الخاص بك:

  1. nano resizeImage.js

أضف الكود التالي لتغيير حجم الصورة إلى عرض 150px وارتفاع 97px:

process_images/resizeImage.js
const sharp = require("sharp");

async function resizeImage() {
  try {
    await sharp("sammy.png")
      .resize({
        width: 150,
        height: 97
      })
      .toFile("sammy-resized.png");
  } catch (error) {
    console.log(error);
  }
}

resizeImage();

ترتبط الدالة resizeImage() بطريقة resize() من وحدة sharp. تأخذ الطريقة كائنًا كوسيط. في الكائن، يتم تعيين أبعاد الصورة التي تريدها باستخدام خاصية width و height. تعيين العرض إلى 150 والارتفاع إلى 97 سيجعل الصورة عرضها 150px، وارتفاعها 97px.

بعد تغيير حجم الصورة، تربط طريقة toFile() من وحدة sharp، والتي تأخذ مسار الصورة كوسيط. تمرير sammy-resized.png كوسيط سيحفظ ملف الصورة بهذا الاسم في دليل العمل لبرنامجك.

الآن، احفظ وأغلق الملف. قم بتشغيل برنامجك في الطرفية:

  1. node resizeImage.js

لن تحصل على أي إخراج، ولكن يجب أن ترى ملف صورة جديد تم إنشاؤه بالاسم sammy-resized.png في دليل المشروع.

افتح الصورة على جهازك المحلي. يجب أن ترى صورة لـ Sammy بعرض 150 بكسل وارتفاع 97 بكسل:

الآن بما أنه يمكنك تغيير حجم الصورة، فيما يلي ستقوم بتحويل تنسيق الصورة المصغرة من png إلى jpeg، ثم ضغط الصورة وحفظها في دليل العمل. للقيام بذلك، ستستخدم طريقة toFormat()، والتي ستقوم بسلسلة بعد طريقة resize().

أضف الكود المظلل لتغيير تنسيق الصورة إلى jpeg وضغطها:

process_images/resizeImage.js
const sharp = require("sharp");

async function resizeImage() {
  try {
    await sharp("sammy.png")
      .resize({
        width: 150,
        height: 97
      })
      .toFormat("jpeg", { mozjpeg: true })
      .toFile("sammy-resized-compressed.jpeg");
  } catch (error) {
    console.log(error);
  }
}

resizeImage();

Dentro de la función resizeImage(), utiliza el método toFormat() del módulo sharp para cambiar el formato de la imagen y comprimirla. El primer argumento del método toFormat() es una cadena que contiene el formato de imagen al que deseas convertir tu imagen. El segundo argumento es un objeto opcional que contiene opciones de salida que mejoran y comprimen la imagen.

Para comprimir la imagen, pásale una propiedad mozjpeg que contenga un valor booleano. Cuando lo configuras en true, sharp utiliza los valores predeterminados de mozjpeg para comprimir la imagen sin sacrificar calidad. El objeto también puede aceptar más opciones; consulta la documentación de sharp para más detalles.

ملاحظة: بخصوص الوسيط الثاني لطريقة toFormat()، كل تنسيق للصورة يأخذ كائنًا به خصائص مختلفة. على سبيل المثال، تقبل خاصية mozjpeg فقط في الصور الـ JPEG.

ومع ذلك، هناك خيارات مكافئة لتنسيقات الصور الأخرى مثل quality، compression، و lossless. تأكد من الرجوع إلى الوثائق لمعرفة أنواع الخيارات المقبولة لتنسيق الصورة التي تقوم بضغطها.

بعد ذلك، قم بتمرير الاسم الملف الجديد لطريقة toFile() لحفظ الصورة المضغوطة باسم sammy-resized-compressed.jpeg.

الآن، قم بحفظ الملف والخروج، ثم قم بتشغيل الشفرة الخاصة بك بالأمر التالي:

  1. node resizeImage.js

لن تتلقى أي إخراج، ولكن سيتم حفظ ملف الصورة sammy-resized-compressed.jpeg في دليل مشروعك.

افتح الصورة على جهازك المحلي وسترى الصورة التالية:

بعد ضغط الصورة الآن، قم بالتحقق من حجم الملف لتأكيد نجاح الضغط. في الطرفية الخاصة بك، قم بتشغيل الأمر du للتحقق من حجم الملف لـ sammy.png:

  1. du -h sammy.png

الخيار -h يولّد إخراجًا قابلًا للقراءة من قبل الإنسان يظهر لك حجم الملف بالـ كيلوبايت، ميغابايت والكثير غيره.

بعد تشغيل الأمر، يجب أن ترى إخراجًا مماثلًا لهذا:

Output
120K sammy.png

يظهر الإخراج أن حجم الصورة الأصلي هو 120 كيلوبايت.

بعد ذلك، قم بالتحقق من حجم الملف لـ sammy-resized.png:

  1. du -h sammy-resized.png

بعد تشغيل الأمر، سترى الإخراج التالي:

Output
8.0K sammy-resized.png

sammy-resized.png هو 8 كيلوبايت بعد أن كان 120 كيلوبايت. هذا يظهر أن عملية التغيير في الحجم تؤثر على حجم الملف.

الآن، تحقق من حجم الملف لـ sammy-resized-compressed.jpeg:

  1. du -h sammy-resized-compressed.jpeg

بعد تشغيل الأمر، سترى الناتج التالي:

Output
4.0K sammy-resized-compressed.jpeg

تم تصغير sammy-resized-compressed.jpeg الآن إلى 4 كيلوبايت من 8 كيلوبايت، مما أدى إلى توفير 4 كيلوبايت، مما يوضح أن الضغط نجح.

الآن بعد أن قمت بتغيير حجم الصورة وتغيير تنسيقها وضغطها، ستقوم بتقطيعها وتحويلها إلى درجات الرمادي.

الخطوة 4 — قص الصورة وتحويلها إلى درجات الرمادي

في هذه الخطوة، ستقوم بقص الصورة وتحويلها إلى درجات الرمادي. قص الصورة هو عملية إزالة المناطق غير المرغوب فيها من الصورة. ستستخدم طريقة extend() لقص صورة sammy.png. بعد ذلك، ستقوم بربط طريقة grayscale() بالصورة المقصوصة وتحويلها إلى درجات الرمادي.

قم بإنشاء وفتح cropImage.js في محرر النصوص الخاص بك:

  1. nano cropImage.js

في ملف cropImage.js الخاص بك، أضف الكود التالي لقص الصورة:

process_images/cropImage.js
const sharp = require("sharp");

async function cropImage() {
  try {
    await sharp("sammy.png")
      .extract({ width: 500, height: 330, left: 120, top: 70  })
      .toFile("sammy-cropped.png");
  } catch (error) {
    console.log(error);
  }
}

cropImage();

تعتبر وظيفة cropImage() وظيفة غير متزامنة تقوم بقراءة صورة وتُرجع الصورة المقصوصة. داخل كتلة try، سيقوم نموذج sharp بقراءة الصورة. ثم، سيقوم الأسلوب extract() المتسلسل المتصل بالنموذج بأخذ كائن يحتوي على الخصائص التالية:

  • width: عرض المنطقة التي ترغب في قصها.
  • height: ارتفاع المنطقة التي ترغب في قصها.
  • top: الوضع الرأسي للمنطقة التي ترغب في قصها.
  • left: الوضع الأفقي للمنطقة التي ترغب في قصها.

عندما تقوم بتعيين width إلى 500 و height إلى 330، فتخيل أن شارب ينشئ صندوقًا شفافًا في الجزء العلوي من الصورة التي ترغب في قصها. أي جزء من الصورة يتناسب مع الصندوق سيظل، وسيتم قص البقية:

تتحكم خصائص top و left في موقع الصندوق. عندما تقوم بتعيين left إلى 120، يتم تحديد موضع الصندوق على بُعد 120 بكسل من الحافة اليسرى للصورة، وعند تعيين top إلى 70، يتم تحديد موضع الصندوق على بُعد 70 بكسل من الحافة العلوية للصورة.

سيتم استخراج المنطقة من الصورة التي تتناسب مع الصندوق وحفظها في sammy-cropped.png كصورة منفصلة.

احفظ واخرج الملف. قم بتشغيل البرنامج في الطرفية:

  1. node cropImage.js

لن يتم عرض الناتج ولكن سيتم حفظ الصورة sammy-cropped.png في دليل مشروعك.

افتح الصورة على جهازك المحلي. يجب أن ترى الصورة مقصوصة:

الآن بعد أن قمت بتقليم الصورة، ستقوم بتحويل الصورة إلى درجات الرمادي. للقيام بذلك، ستقوم بربط الطريقة grayscale بالمثيل sharp. أضف الكود المظلل لتحويل الصورة إلى درجات الرمادي:

process_images/cropImage.js
const sharp = require("sharp");

async function cropImage() {
  try {
    await sharp("sammy.png")
      .extract({ width: 500, height: 330, left: 120, top: 70 })
      .grayscale()
      .toFile("sammy-cropped-grayscale.png");
  } catch (error) {
    console.log(error);
  }
}

cropImage();

تقوم وظيفة cropImage() بتحويل الصورة المقتطفة إلى درجات الرمادي عن طريق ربط طريقة grayscale() من وحدة sharp بالمثيل sharp. ثم تحفظ الصورة في دليل المشروع باسم sammy-cropped-grayscale.png.

اضغط على CTRL+X لحفظ والخروج من الملف.

قم بتشغيل الكود الخاص بك في الطرفية:

  1. node cropImage.js

افتح sammy-cropped-grayscale.png على جهازك المحلي. يجب أن ترى الصورة الآن باللونين الرمادي والأبيض:

الآن بعد أن قمت بتقليم واستخراج الصورة، ستعمل على تدويرها وتشويهها.

الخطوة 5 — تدوير وتشويه الصور

في هذه الخطوة، ستقوم بتدوير صورة sammy.png بزاوية 33 درجة. ستقوم أيضًا بتطبيق تشويش جاوسيان على الصورة المدارة. التشويش الجاوسيان هو تقنية لتشويش الصورة باستخدام الدالة الجاوسيانية، والتي تقلل من مستوى الضوضاء والتفاصيل في الصورة.

أنشئ ملف rotateImage.js في محرر النصوص الخاص بك:

  1. nano rotateImage.js

في ملفك rotateImage.js، اكتب الكود التالي لإنشاء دالة تدور الصورة sammy.png بزاوية 33 درجة:

process_images/rotateImage.js
const sharp = require("sharp");

async function rotateImage() {
  try {
    await sharp("sammy.png")
      .rotate(33, { background: { r: 0, g: 0, b: 0, alpha: 0 } })
      .toFile("sammy-rotated.png");
  } catch (error) {
    console.log(error);
  }
}

rotateImage();

الدالة rotateImage() هي دالة غير متزامنة تقرأ صورة وستعيد الصورة مدورة بزاوية 33 درجة. ضمن الدالة، تأخذ الدالة rotate() من وحدة الشارب (sharp) معاملين. المعامل الأول هو زاوية الدوران 33 درجة. بشكل افتراضي، يجعل sharp خلفية الصورة المدورة سوداء. لإزالة الخلفية السوداء، تمرر كائنًا كمعامل ثاني لجعل الخلفية شفافة.

الكائن يحتوي على خاصية background التي تحتوي على كائن يحدد نموذج الألوان RGBA. تعني RGBA الأحمر، الأخضر، الأزرق، والألفا.

  • r: يتحكم في شدة اللون الأحمر. يقبل قيمة صحيحة بين 0 و 255. 0 يعني عدم استخدام اللون، و 255 هو أحمره عند أقصى درجة.

  • g: يتحكم في شدة اللون الأخضر. يقبل قيمة صحيحة بين 0-255. 0 يعني عدم استخدام اللون الأخضر، و 255 هو أخضره عند أقصى درجة.

  • b: يتحكم في شدة اللون الأزرق. يقبل أيضًا قيمة صحيحة بين 0 و 255. 0 يعني عدم استخدام اللون الأزرق، و 255 هو أزرقه عند أقصى درجة.

  • ألفا: تحكم في الشفافية للون المعرف بخصائص ار، جي، و بي. 0 أو 0.0 يجعل اللون شفافًا و 1 أو 1.1 يجعل اللون غير شفاف.

لجعل خاصية ألفا تعمل، يجب التأكد من تعريف وتعيين القيم لـ ار، جي، و بي. تعيين قيم ار، جي، و بي إلى 0 ينشئ لونًا أسود. لإنشاء خلفية شفافة، يجب عليك تعريف لون أولاً، ثم يمكنك تعيين ألفا إلى 0 لجعله شفافًا.

الآن، احفظ واخرج من الملف. قم بتشغيل البرنامج النصي في الطرفية:

  1. node rotateImage.js

تحقق من وجود sammy-rotated.png في دليل المشروع الخاص بك. افتحه على جهازك المحلي.

يجب أن ترى الصورة مدارة لزاوية 33 درجة:

بعد ذلك، ستقوم بتحريك الصورة المدارة. ستحقق ذلك عن طريق ربط طريقة blur() بالمثيل sharp.

أدخل الكود المظلل أدناه لتحريك الصورة:

process_images/rotateImage.js
const sharp = require("sharp");

async function rotateImage() {
  try {
    await sharp("sammy.png")
      .rotate(33, { background: { r: 0, g: 0, b: 0, alpha: 0 } })
      .blur(4)
      .toFile("sammy-rotated-blurred.png");
  } catch (error) {
    console.log(error);
  }
}

rotateImage();

تقوم وظيفة rotateImage() الآن بقراءة الصورة، وتدويرها، وتطبيق تمويه غوسيان على الصورة. يتم تطبيق تمويه غوسيان على الصورة باستخدام الأسلوب blur() في وحدة الشحذ. يقبل الأسلوب وسيطة واحدة تحتوي على قيمة سيجما بين 0.3 و 1000. تمرير 4 سيطبق تمويه غوسيان بقيمة سيجما 4. بعد أن تتم تمويه الصورة، تقوم بتعريف مسار لحفظ الصورة المطفية.

سيقوم البرنامج النصي الخاص بك الآن بتطفيش الصورة المدوّرة بقيمة سيجما 4. حفظ وخروج من الملف، ثم تشغيل البرنامج النصي في الطرفية الخاصة بك:

  1. node rotateImage.js

بعد تشغيل البرنامج النصي، افتح ملف sammy-rotated-blurred.png على جهازك المحلي. يجب أن ترى الصورة المدوّرة مطفية الآن:

الآن بعد أن قمت بتدوير الصورة وتطفيشها، ستقوم بتركيب صورة فوق الأخرى.

الخطوة 6 — تركيب الصور باستخدام composite()

التركيب الصوري هو عملية دمج صورتين أو أكثر منفصلتين لإنشاء صورة واحدة. يتم ذلك لإنشاء تأثيرات تستعير أفضل عناصر الصور المختلفة. حالة استخدام شائعة أخرى هي وضع علامة مائية على الصورة بشعار.

في هذا القسم، ستقوم بدمج “sammy-transparent.png” فوق “underwater.png”. سيخلق هذا وهمًا بأن “سامي” يسبح في عمق المحيط. لدمج الصور، ستقوم بسلسلة الأسلوب “composite()” إلى مثيل “sharp”.

أنشئ وافتح الملف “compositeImage.js” في محرر النص الخاص بك:

  1. nano compositeImages.js

الآن، أنشئ وظيفة لدمج الصورتين عن طريق إضافة الكود التالي في ملف “compositeImages.js”:

process_images/compositeImages.js
const sharp = require("sharp");

async function compositeImages() {
  try {
    await sharp("underwater.png")
      .composite([
        {
          input: "sammy-transparent.png",
          top: 50,
          left: 50,
        },
      ])
      .toFile("sammy-underwater.png");
  } catch (error) {
    console.log(error);
  }
}

compositeImages()

تقوم وظيفة “compositeImages()” بقراءة صورة “underwater.png” أولاً. بعد ذلك، تقوم بسلسلة الأسلوب “composite()” من وحدة “sharp”، والتي تأخذ مصفوفة كوسيط. تحتوي المصفوفة على كائن واحد يقرأ صورة “sammy-transparent.png”. الكائن له الخصائص التالية:

  • input: يأخذ مسار الصورة التي تريد دمجها فوق الصورة المعالجة. كما يقبل أيضًا Buffer، Uint8Array، أو Uint8ClampedArray كمدخل.
  • top: يتحكم في الموضع الرأسي للصورة التي تريد دمجها. بتعيين top إلى 50، تنقل الصورة “sammy-transparent.png” بمقدار 50 بكسل من حافة الأعلى لصورة “underwater.png”.
  • left: يتحكم في الوضع الأفقي للصورة التي ترغب في دمجها فوق أخرى. يعيّن left إلى 50 ينقل sammy-transparent.png بمقدار 50 بكسل من الحافة اليسرى لصورة underwater.png.

تتطلب طريقة composite() صورة من حجم مماثل أو أصغر للصورة المعالجة.

لتصور ما تقوم به طريقة composite()، فكر فيها وكأنها تنشئ كومة من الصور. يتم وضع صورة sammy-transparent.png فوق صورة underwater.png:

قيم top و left توضع الصورة sammy-transparent.png نسبيًا إلى صورة underwater.png.

احفظ النص الخاص بك واخرج من الملف. قم بتشغيل النص الخاص بك لإنشاء تكوين صورة:

node compositeImages.js

افتح sammy-underwater.png على جهازك المحلي. يجب أن ترى الآن sammy-transparent.png مُدمجة فوق صورة underwater.png:

لقد قمت الآن بدمج الصور باستخدام طريقة composite(). في الخطوة التالية، ستستخدم طريقة composite() لإضافة نص إلى صورة.

الخطوة 7 — إضافة نص على صورة

في هذه الخطوة، ستكتب نصًا على صورة. في وقت الكتابة، لا يتوفر لـ sharp طريقة أصلية لإضافة نص إلى صورة. لإضافة نص، أولاً، ستقوم بكتابة الشيفرة لرسم النص باستخدام Scalable Vector Graphics(SVG). بمجرد إنشاء صورة SVG، ستقوم بكتابة الشيفرة لتركيب الصورة مع صورة sammy.png باستخدام الطريقة composite.

SVG هي لغة وسمية قائمة على XML تُستخدم لإنشاء رسوميات متجهية للويب. يمكنك رسم نص، أو أشكال مثل الدوائر، والمثلثات، وكذلك رسم أشكال معقدة مثل الرسوم التوضيحية، والشعارات، إلخ. تتم إنشاء الأشكال المعقدة بواسطة أداة رسومية مثل Inkscape التي تولد شيفرة SVG. يمكن تقديم أشكال SVG وتحجيمها إلى أي حجم دون فقدان الجودة.

قم بإنشاء وفتح ملف addTextOnImage.js في محرر النصوص الخاص بك.

  1. nano addTextOnImage.js

في ملف addTextOnImage.js الخاص بك، قم بإضافة الشيفرة التالية لإنشاء حاوية SVG:

process_images/addTextOnImage.js
const sharp = require("sharp");

async function addTextOnImage() {
  try {
    const width = 750;
    const height = 483;
    const text = "Sammy the Shark";

    const svgImage = `
    <svg width="${width}" height="${height}">
    </svg>
    `;
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage();

تعرف الدالة addTextOnImage() أربع متغيرات: width، height، text، و svgImage. width تحمل القيمة الصحيحة 750، و height تحمل القيمة الصحيحة 483. text تحمل النص Sammy the Shark. هذا هو النص الذي سترسمه باستخدام SVG.

المتغير svgImage يحمل عنصر svg. يحتوي عنصر svg على خاصيتين: width و height التي تتفاعل مع المتغيرات width و height التي قمت بتعريفها سابقًا. ينشئ عنصر svg حاوية شفافة وفقًا للعرض والارتفاع المعطيين.

لقد قمت بإعطاء عنصر svg عرضًا بقيمة 750 وارتفاعًا بقيمة 483 بحيث يكون حجم صورة SVG مماثلًا لـ sammy.png. سيساعد هذا في جعل النص يبدو متوسطًا على صورة sammy.png.

بعد ذلك، ستقوم برسم النصوص الرسومية. أضف الكود المظلل لرسم Sammy the Shark على حاوية SVG:

process_images/addTextOnImage.js
async function addTextOnImage() {
    ...
    const svg = `
    <svg width="${width}" height="${height}">
    <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;
  ....
}

يحتوي عنصر text في SVG على أربعة سمات: x، y، text-anchor، و class. تعريف x و y يحدد الموضع للنص الذي تقوم برسمه على حاوية SVG. تمثل سمة x موضع النص أفقيًا، وتمثل سمة y موضع النص رأسيًا.

تعيين x إلى 50% يرسم النص في منتصف الحاوية على محور x، وتعيين y إلى 50% يوضع النص في المنتصف على محور y لصورة SVG.

سمة text-anchor تُحاكي النص أفقيًا. تعيين text-anchor إلى middle سيوضح النص في المنتصف عند الإحداثي x الذي حددته.

class يحدد اسم الفئة على عنصر text. ستستخدم اسم الفئة لتطبيق أنماط CSS على عنصر text.

${text} يعيد سلسلة Sammy the Shark المخزنة في متغير text. هذه هي النص الذي سيتم رسمه على صورة SVG.

ثم، أضف الشيفرة المظللة لتنسيق النص باستخدام CSS:

process_images/addTextOnImage.js
    const svg = `
    <svg width="${width}" height="${height}">
      <style>
      .title { fill: #001; font-size: 70px; font-weight: bold;}
      </style>
      <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;

في هذه الشيفرة، fill يغير لون النص إلى الأسود، font-size يغير حجم الخط، وfont-weight يغير وزن الخط.

في هذه النقطة، قمت بكتابة الشيفرة اللازمة لرسم النص Sammy the Shark باستخدام SVG. بعد ذلك، ستقوم بحفظ صورة SVG كـpng بواسطة شارب حتى تتمكن من رؤية كيفية رسم SVG للنص. بمجرد الانتهاء من ذلك، ستقوم بدمج صورة SVG مع sammy.png.

أضف الشيفرة المظللة لحفظ صورة SVG كـpng بواسطة شارب:

process_images/addTextOnImage.js
    ....
    const svgImage = `
    <svg width="${width}" height="${height}">
    ...
    </svg>
    `;
    const svgBuffer = Buffer.from(svgImage);
    const image = await sharp(svgBuffer).toFile("svg-image.png");
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage();

Buffer.from() ينشئ كائن Buffer من صورة SVG. البوفر هو مساحة مؤقتة في الذاكرة تخزن البيانات الثنائية.

بعد إنشاء كائن الوسيط، قم بإنشاء مثيل sharp باستخدام كائن الوسيط كإدخال. بالإضافة إلى مسار الصورة، يقبل sharp أيضًا buffer, Uint9Array, أو Uint8ClampedArray.

أخيرًا، قم بحفظ صورة SVG في دليل المشروع بمسمى svg-image.png.

إليك الشفرة الكاملة:

process_images/addTextOnImage.js
const sharp = require("sharp");

async function addTextOnImage() {
  try {
    const width = 750;
    const height = 483;
    const text = "Sammy the Shark";

    const svgImage = `
    <svg width="${width}" height="${height}">
      <style>
      .title { fill: #001; font-size: 70px; font-weight: bold;}
      </style>
      <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;
    const svgBuffer = Buffer.from(svgImage);
    const image = await sharp(svgBuffer).toFile("svg-image.png");
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage()

احفظ واخرج الملف، ثم قم بتشغيل السكربت الخاص بك باستخدام الأمر التالي:

node addTextOnImage.js

ملاحظة: إذا قمت بتثبيت Node.js باستخدام الخيار 2 — تثبيت Node.js باستخدام Apt باستخدام مستودع NodeSource PPA أو الخيار 3 — تثبيت Node باستخدام Node Version Manager وواجهت الخطأ fontconfig error: cannot load default config file: no such file: (null)، قم بتثبيت fontconfig لإنشاء ملف تكوين الخط.

قم بتحديث فهرس حزم الخادم الخاص بك، وبعد ذلك، استخدم apt install لتثبيت fontconfig.

  1. sudo apt update
  2. sudo apt install fontconfig

افتح svg-image.png على جهازك المحلي. يجب أن ترى النص Sammy the Shark مقدمًا بخلفية شفافة:

الآن بعد تأكيد أن رمز SVG يرسم النص، سوف تقوم بدمج الرسومات النصية على sammy.png.

أضف الكود المشار إليه باللون البارز التالي لدمج صورة الرسومات النصية SVG على صورة sammy.png.

process_images/addTextOnImage.js
const sharp = require("sharp");

async function addTextOnImage() {
  try {
    const width = 750;
    const height = 483;
    const text = "Sammy the Shark";

    const svgImage = `
    <svg width="${width}" height="${height}">
      <style>
      .title { fill: #001; font-size: 70px; font-weight: bold;}
      </style>
      <text x="50%" y="50%" text-anchor="middle" class="title">${text}</text>
    </svg>
    `;
    const svgBuffer = Buffer.from(svgImage);
    const image = await sharp("sammy.png")
      .composite([
        {
          input: svgBuffer,
          top: 0,
          left: 0,
        },
      ])
      .toFile("sammy-text-overlay.png");
  } catch (error) {
    console.log(error);
  }
}

addTextOnImage();

الطريقة composite() تقرأ صورة SVG من متغير svgBuffer، وتوضع 0 بكسل من الأعلى، و 0 بكسل من الحافة اليسرى لصورة sammy.png. بعد ذلك، قم بحفظ الصورة المدمجة باسم sammy-text-overlay.png.

احفظ وأغلق الملف الخاص بك، ثم قم بتشغيل البرنامج باستخدام الأمر التالي:

  1. node addTextOnImage.js

افتح sammy-text-overlay.png على جهازك المحلي. يجب أن ترى النص المضاف فوق الصورة:

لقد استخدمت الآن الطريقة composite() لإضافة نص تم إنشاؤه بواسطة SVG على صورة أخرى.

الختام

في هذه المقالة، تعلمت كيفية استخدام طرق حادة لمعالجة الصور في Node.js. أولاً، قمت بإنشاء مثيل لقراءة صورة واستخدمت طريقة metadata() لاستخراج بيانات الصورة الوصفية. ثم استخدمت الطريقة resize() لتغيير حجم الصورة. بعد ذلك، استخدمت الطريقة format() لتغيير نوع الصورة وضغطها. بعد ذلك، تقدمت إلى استخدام طرق حادة مختلفة لقص الصورة، وتحويلها إلى درجات الرمادي، وتدويرها، وتشويشها. وأخيرًا، استخدمت الطريقة composite() لدمج الصورة وإضافة نص على الصورة.

لمزيد من الفهم حول الطرق الحادة الإضافية، قم بزيارة توثيق sharp. إذا كنت ترغب في متابعة تعلم Node.js، انظر كيفية البرمجة في سلسلة Node.js.

Source:
https://www.digitalocean.com/community/tutorials/how-to-process-images-in-node-js-with-sharp