5.21. التحجيم والقلب والاقتصاص¶
كانت الأقسام الفرعية السابقة جميعها تعمل على البكسلات في المواضع نفسها التي بدأت فيها. أما عائلة التحويل فتغيّر ذلك. فالتحجيم يرسل كل بكسل دخل إلى موضع خرج مختلف، وربما إلى عدة مواضع خرج في آنٍ واحد (عند التكبير) أو إلى موضع يتشاركه مع عدة بكسلات دخل أخرى (عند التصغير). والقلب والدوران يفعلان الشيء نفسه عبر ربط مختلف. أما الاقتصاص فيُبقي مجموعة فرعية مستطيلة من بكسلات الدخل ويتجاهل الباقي.
تكشف وحدة image تلك العائلة عبر ثلاث طرق تتشارك معظم وسيطاتها ومعظم سلوكها:
copy()-- تُنتج نسخة من الصورة، ربما مُحجَّمة أو مُقتصَّة أو مُعاد توجيهها.crop()-- العملية نفسها التي تجريهاcopy، لكن مع توقع أن يختار التطبيق مستطيلاً فرعياً من المصدر.scale()-- العملية نفسها مجدداً، مع توقع أن يعيد التطبيق تحجيم النتيجة.
تتشارك الطرق الثلاث الوسيطات نفسها وآلية التحويل نفسها؛ والفرق هو في المكان الذي تستقر فيه النتيجة افتراضياً. فـ copy() تُنتج صورة جديدة، بينما تعدّل crop() و scale() المصدرَ في مكانه.
5.21.2. الاستيفاء: AREA و BILINEAR و BICUBIC¶
عندما يرسل التحجيم كل بكسل خرج إلى موضع لا يتوافق مع أي بكسل دخل وحيد، يتعين على الطريقة أن تختار القيمة التي ستكتبها. وتتحكم ثلاثة أعلام في الكيفية:
تستوفي image.BILINEAR بين بكسلات الدخل الأربعة الأقرب مع ترجيحها بمسافتها من موضع الخرج. والنتيجة أنعم من أقرب جار، بلا أي تسنّن مرئي على الخطوط القُطرية، لكن الحساب الإضافي يكلّف نحو أربعة أضعاف مرور أقرب جار. وهو الخيار الصحيح لمعظم أعمال التكبير ولأي معامل تحجيم غير صحيح.
تستوفي image.BICUBIC بين بكسلات الدخل الستة عشر الأقرب باستخدام منحنى تكعيبي، مما يُنتج نتائج أنعم بعدُ على حساب مزيد من الحساب مجدداً. وهي أفضل جودة للتطبيقات الحساسة للتكلفة التي تحتاجها؛ ونادراً ما تستحق الحساب الإضافي للإطارات الحية التي لن تعرضها سوى OpenMV IDE.
تأخذ image.AREA متوسط كل بكسل دخل يقع داخل بصمة بكسل الخرج -- وهي الخوارزمية الصحيحة للتصغير. والاستيفاءان الثنائي الخطي والتكعيبي الثنائي مستوفيان: فهما يقدّران قيمة بين بكسلات المصدر، وهو ما يحتاجه التكبير، لكن عند التصغير يغطي كل بكسل خرج بكسلات مصدر كثيرة، ولا يقرأ المستوفي إلا القليل منها الأقرب -- والتفصيل الذي يتخطاه يعود على هيئة تسنّن. أما image.AREA فتطوي كل بكسل مغطّى في المتوسط بدلاً من ذلك.
خوارزمية التحجيم الافتراضية بدون أي تلميح هي أقرب جار، وهي الأرخص والإجابة الصحيحة عندما يكون المصدر أصلاً عند دقة بكسلات الوجهة.
5.21.3. التوجيه: القلبات والدورانات¶
أعلام التوجيه مجموعة صغيرة من التحويلات المنطقية التي تتركب بحرية بعضها مع بعض ومع أعلام الاستيفاء:
تقلب
image.VFLIPالصورة رأسياً (الأعلى يصبح الأسفل).تعكس
image.HMIRRORالصورة أفقياً (اليسار يصبح اليمين).تبدّل
image.TRANSPOSEالمحورين x و y (الصفوف تصبح أعمدة).
تأتي معظم الدورانات من تركيب تلك الثلاثة. وتكشف الوحدة أيضاً اختصارات مسماة:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
في الشيفرة:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. معالجة نسبة الأبعاد¶
عندما لا تطابق نسبة أبعاد المصدر المستطيلَ الذي يُرسم فيه، تقرر ثلاثة أعلام ما الذي يُفعل بعدم التطابق:
تحفظ image.SCALE_ASPECT_KEEP نسبةَ أبعاد المصدر و تضع أشرطة سوداء حول النتيجة -- إذ يُحجَّم المصدر حتى يتسع داخل الوجهة، مع ملء بقية الوجهة ببكسلات فارغة (صفرية). وهو الخيار الصحيح عندما يكون الحفاظ على المصدر غير مشوَّه أهمَّ من ملء الخرج كله.
تحفظ image.SCALE_ASPECT_EXPAND نسبةَ أبعاد المصدر و تقتصّه -- إذ يُحجَّم المصدر حتى يملأ الوجهة، مع قطع الأجزاء التي تمتد خارج الوجهة. وهو الخيار الصحيح عندما يكون ملء الخرج كله أهمَّ من رؤية كل جزء من المصدر.
تتجاهل image.SCALE_ASPECT_IGNORE نسبةَ الأبعاد وتمطّ المصدر ليملأ الوجهة، قابلةً بأي تشويه يُحدثه ذلك. وهو الخيار الصحيح عندما يكون التطبيق قد راعى التشويه أصلاً -- عندما لا تكون أبعاد الوجهة في الواقع مستطيلاً للمشهد نفسه، على سبيل المثال.
الوضع الافتراضي (بلا ضبط أي علم لنسبة الأبعاد) هو نفسه SCALE_ASPECT_IGNORE: المطّ للملء. والتطبيقات التي تهتم بنسبة الأبعاد تحدد واحداً من الثلاثة صراحةً.
5.21.5. متى تختار كلاً منها¶
تستخدم معظم عمليات تغيير الحجم scale() مع زوج x_scale / y_scale وتلميح استيفاء:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
تستخدم معظم الدورانات الاستدعاء نفسه مع hint=image.ROTATE_90 أو ما شابه.
يستخدم الاقتصاص crop() مع roi غير افتراضي:
img.crop(roi=(40, 30, 200, 150))
عندما يتعين أن ينجو المصدر من العملية -- التقاط إطار مرجعي، أو أخذ صورة مصغرة لإطار على وشك أن يُعالَج بطريقة مُتلِفة -- تُنتج copy() النتيجةَ كصورة جديدة وتترك المصدر سالماً:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
ذلك الوضع الافتراضي هو الفرق الحقيقي وراء الأسماء الثلاثة: فـ scale و crop تحولان في المكان، بينما تخصص copy. وتسدّ وسيطات وضع النتيجة الفجوة: فـ copy=True على scale أو crop تخصص النتيجة كمخزن منفصل في الكومة بدلاً من الكتابة فوق المصدر، و copy_to_fb=True على أي من الثلاث تُنزلها في مخزن الإطارات لمعاينة OpenMV IDE.