بسم الله الرحمن الرحيم

مقدمة

سواءً سمعت بها أم لا، الرسومات المتّجهية القابلة للتمديد (أو SVG) من العناصر الرئيسية في واجهات المستخدم الحديثة. باختصار، هي صيغة صور تعتمد على المتّجهات بدلاً من البكسلات، ما يجعلها قابلةً للتكبير والتصغير بلا نهاية. هذا يجعلها مثاليةً لكلّ ما يحتاج للمرونة في عرضه على أحجام الشاشات المختلفة.

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

سأبدأ بمقدّمةٍ مختصرةٍ عن الرسومات المتّجهية. يمكنك تجاوزها إن كان هذا المصطلح مألوفاً لديك باستعمال فهرس المحتوى بالأعلى.

ملخص للرسومات المتّجهية

الفكرة العامة

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

على سبيل المثال، إحدى الدوال الرئيسية في الرسومات المتّجهية هي LineTo أو “خط إلى”، دالةٌ ذات متغيّرين أحدهما نقطة بداية الخط والآخر نهايته. إن كنت تذكر ما تعلمته في المدرسة من حصة الرياضيات ستعلم أن دالة الخط المستقيم يمكن إنشاؤها من نقطتين فقط على هذا الخط، وصيغتها $b + mx = y$. يمكننا بعدها استعمال هذه الدالة لتدلّنا على أماكن النقاط الأخرى على هذا الخط إلى ما لا نهاية. كما ترى، للدوال من اسمها نصيب!

التعريف

الآن يمكننا إلقاء نظرةٍ على كيفية تعريف الرسومات المتّجهية. حتى الآن تكلمنا فقط عن الجانب الرياضي من القصة، إلّا أن الرسومات المتّجهية تميل إلى عالم الرموز والحروف أكثر من عالم الأرقام، بالتحديد إلى عالم لغة التوصيف القابلة للتوسعة (أو XML). هذه اللغة شبيهة جداً بلغة توصيف النص الفائق (أو HTML).

<svg xmlns="http://www.w3.org/2000/svg">
  <path
    fill="none"
    stroke="red"
    d="M 10,30
       A 20,20 0,0,1 50,30
       A 20,20 0,0,1 90,30
       Q 90,60 50,90
       Q 10,60 10,30 z
       " />
</svg>

كما ترى، الصيغة تتكوّن من علامات (tags) وصفات (attributes). العلامة الخارجية <svg> تشير إلى أن الآتي شيفرة رسومات متّجهية، بينما <path> تبدأ بتعريف الشكل الفعلي للرسمة. هكذا يمكنك تخيّل الرسومات المتّجهية على أنها مجموعة تعليمات أو “مخطط” لرسم شكلٍ ما، يعمل على ترجمتها محرّك رسم (يحتوى عليه كل متصفّح حديث). <path> تخبر المحرّك ماذا يرسم، وبالتحديد الصفة “d” تحتوي أوامر الرسم. هذه الأوامر أشبه بالدّوال التي سبق وتحدثنا عنها، والأرقام أمام الأوامر هي معطيات هذه الدوال على شكل أزواج إحداثيات $x,y$.

كيف تبدو الرسمة التي بالأعلى إذاً؟ انظر:

لرؤية الأوامر كلها اقرأ هذه الصفحة. غالباً ستجد في مستندات موزيلا كل ما تريد معرفته عن الرسومات المتّجهية وأكثر، ولكن فهم ما تحدثنا عنه حتى الآن كافٍ لما سنقوم به بعد قليل.

العمليات على الرسومات المتّجهية

التضمين

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

ربما عندك خلفيةٌ عن تحرير الصور وعمليات التلاعب بها، والتي هي عبارةٌ عن عملياتٍ رياضيّة تُطبّق على بكسلات الصورة. قد يريد أحدهم استعمال إحدى تلك العمليات المجرّبة على الرسومات المتّجهية للوصول إلى نتائج أفضل من الرسومات التي لديه، لكن هذا يضعنا أمام العائق الأول في رحلتنا: الرسومات المتّجهية حروفٌ وليست أرقاماً. هذا يعني أنّنا بحاجةٍ إلى طريقةٍ للتحويل من فضاء الرسومات المتّجهية إلى فضاء الأرقام إن صح التعبير، ومن ثمّ نطبّق عملياتنا الحسابية عليها، وبعدها نعيدها إلى أصلها، حبّذا دون خسارة معلوماتها الأصلية. فلنسمّي هذا التحويل تضميناً للرسومات المتّجهية.

قد تكون الوصول لهذا التضمين أصعب ممّا تتوقع. أولاً، نحن بحاجةٍ للتفريق بدقّة بين علامات الرسومات المتّجهية المنفصلة. بعد ذلك، يجب أن نحافظ على صفة كل واحدةٍ منها بدقّة، الصفات التي كثيراً ما تكون من فئاتٍ منفصلة هي الأخرى. من البديهي أيضاً أن هذا التضمين تقابُلي.

فلنجرب بعض الأفكار.

إعادة الرسم

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

أولاً، استعمال دالّة واحدةٍ (متسلسلة فورييه) هكذا يُنتج رسمةً متّصلة، على عكس الكثير من الرسومات المتّجهية التي تحوي عادةً على عدة أجزاءٍ منفصلة. زد على ذلك اعتماد متسلسلة فورييه على اختيار عيّناتٍ من النقاط الواقعة على خطوط الرسومات المتّجهية، كما في عملية تحويل التسجيلات التشابهية إلى رقمية (Analog to Digital Audio)، وهي عمليةٌ يعيبها خسارة جزءٍ من المعلومات الأصلية. يمكن القول بأنّه باستعمال عددٍ كافٍ من العيّنات لن يُمكن للعين البشرية التفريق بين الرسمة الأصلية والرسمة التي سننتجها نحن بمتسلسلة فورييه، بيد أنّ طبيعة رسمتنا الناقصة لا يمكن إنكارها.

دعونا نكمل البحث تاركين متسلسلة فورييه كخطةٍ بديلة على أمل العثور على أسلوبٍ أفضل.

المشفّر التلقائي (Autoencoder)

المشفّر التلقائي بنية ذكاء اصطناعي من الأكثر شيوعاً، ويمكنك العثور على شروحاتٍ وافرة لها بعد بحثٍ سريع (ربما بالإنجليزية فقط). باختصار، دور هذا النموذج (Model) هو تعلّم تمثيلٍ خفي وفريد لمجموعة بيانات، تمثيلٌ لها فائدةٌ ما بشكلٍ أو بآخر (مثلاً يستعمل بياناتٍ أقل) وغالباً ما يكون استعماله مع الصور.

المصدر. رسم بياني مبسّط للمشفّر التلقائي. يمكنه تشفير وفك تشفير البيانات التي تدرّب عليها، والبيانات المضغوطة (compressed data) هي التمثيل الخفيّ الذي نتحدث عنه.

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

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

هذا لا يؤثر بشكلٍ ملحوظ على القدرة التعبيرية للرسومات المتّجهية حيث يمكن تحويل كل الأشكال الأساسية إلى مجموعةٍ من الخطوط المستقيمة ومنحنيات بيزيه. لدينا الرسمة المتّجهية V:

$V = \{P_1, . . . , P_{N_P} \}$

كمجموعة $_PN$ من الخطوط $_iP$. كل خط يمكن تعريفه كثلاثي

$P_i =(S_i , f_i, v_i)$

حيث ترمز $_iv ∈ \{0, 1\}$ إلى كون الخط خفيّاً أم ظاهراً و $_if ∈ \{0, 1, 2\}$ إلى لون الخط.

$S_i = (C^1_i, . . . , C^{N_C}_i)$

تحوي مجموعة $_CN$ من الأوامر $^j_iC$. الأمر $(^j_ic, ^j_iX) = ^j_iC$ بدوره يمكن تعريفه على أنّه واحدٍ من هذه الأنواع $^j_ic ∈ \{\text{\textless}SOS\text{\textgreater}, m, l, c, z, \text{\textless}EOS\text{\textgreater} \}$ بالإضافة إلى مجموعة معطيات.

على أن هذا ما زال تمثيلاً منفصلاً للرسمة، نحتاج إلى تضمينٍ بالأرقام لنعطيه للمشفّر. الباحثون من DeepSVG استعملوا تضميناً لكل أمر $^j_iC$ على شكل المتّجه $ ^j_ie = ^j_{cmd,i}e + ^j_{coord,i}e + ^j_{ind,i}e $ ، والذي يتكون بدوره من ثلاثة تضمينات مختلفة: تضمين الأمر $^j_{cmd,i}e$ ، وتضمين الإحداثيات $^j_{coord,i}e$ ، وتضمين المؤشّر $^j_{ind,i}e$. التفاصيل الحسايبة لكل تضمين وطريقة جمعها في متّجهٍ واحد أطول من أن نخوض فيها في هذه التدوينة ويمكن الإطلاع عليها في الصفحة 5 من الورقة البحثية تحت عنوان “Embedding”.

هكذا نحصل على المتّجه الخفي صاحب الـ256 بعداً كمادّةٍ نطوِّعها بين أيدينا كيف نشاء. على سبيل المثال، طرح وإضافة قيم للمتّجه هذا قد يؤدي إلى مجموعة لا تحصى من النتائج، انظر:

المصدر. هذه كلها صور متحركة لنسخ مختلفة من الرسمة نفسها.

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

آخذين تمثيلها المبسّط على النظام الإحداثي كنقطة بداية سيبدو الأمر هكذا:

النقاط في الزاوية السفلية اليسرى هي قيم -1 لا فائدة لها، يمكن تجاهلها.

تبدو تماماً كالحركة العشوائية لجزيئات الموائع عند تسخينها، والذي كان من مصادر الإلهام في نماذج الانتشار (Diffusion Models) التي تُستعمل في إنتاج الصور، إن لم أكن مخطئاً، ولكن هذه قصة تدوينة أخرى. هذا المشفّر يفتح إمكانية تحرير الصور المتّجهة فعلاً كما نفعل بالصور البكسيلة، وإمكانية إنشاء رسوم متحركة في هذه الصيغة، إلى جانب العديد من الأمور الفريدة الأخرى! سأختم هذه التدوينة الطويلة ببضعة أمثلة.


أراكم لاحقاً وآمل أن تكونوا استفدتم شيئاً من هذه التدوينة.